@kitsy/cnos-ui 1.9.0 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/App.tsx +95 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitsy/cnos-ui",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "Vite-powered React UI for browsing CNOS configuration state.",
5
5
  "private": false,
6
6
  "type": "module",
package/src/App.tsx CHANGED
@@ -104,6 +104,7 @@ export function App() {
104
104
  const [inspectPayload, setInspectPayload] = useState<InspectPayload | null>(null);
105
105
  const [selectedWorkspace, setSelectedWorkspace] = useState('');
106
106
  const [selectedProfile, setSelectedProfile] = useState('');
107
+ const [secretPassphrase, setSecretPassphrase] = useState('');
107
108
  const [namespace, setNamespace] = useState<NamespaceTab>('value');
108
109
  const [prefix, setPrefix] = useState('');
109
110
  const [inspectKey, setInspectKey] = useState('value.app.name');
@@ -111,6 +112,8 @@ export function App() {
111
112
  const [loading, setLoading] = useState(true);
112
113
  const [loadingList, setLoadingList] = useState(true);
113
114
  const [loadingInspect, setLoadingInspect] = useState(false);
115
+ const [revealingList, setRevealingList] = useState(false);
116
+ const [revealingInspect, setRevealingInspect] = useState(false);
114
117
  const deferredPrefix = useDeferredValue(prefix);
115
118
 
116
119
  function buildSelectionQuery(): string {
@@ -233,6 +236,66 @@ export function App() {
233
236
  }
234
237
  }
235
238
 
239
+ async function revealSecretList(): Promise<void> {
240
+ setRevealingList(true);
241
+ setError(null);
242
+
243
+ try {
244
+ const payload = await fetch('/api/reveal/list', {
245
+ method: 'POST',
246
+ headers: {
247
+ 'Content-Type': 'application/json',
248
+ },
249
+ body: JSON.stringify({
250
+ workspace: selectedWorkspace,
251
+ profile: selectedProfile,
252
+ prefix: deferredPrefix,
253
+ passphrase: secretPassphrase,
254
+ }),
255
+ });
256
+
257
+ if (!payload.ok) {
258
+ throw new Error(await payload.text());
259
+ }
260
+
261
+ setListPayload((await payload.json()) as ListPayload);
262
+ } catch (err: unknown) {
263
+ setError(err instanceof Error ? err.message : String(err));
264
+ } finally {
265
+ setRevealingList(false);
266
+ }
267
+ }
268
+
269
+ async function revealSecretInspect(): Promise<void> {
270
+ setRevealingInspect(true);
271
+ setError(null);
272
+
273
+ try {
274
+ const response = await fetch('/api/reveal/inspect', {
275
+ method: 'POST',
276
+ headers: {
277
+ 'Content-Type': 'application/json',
278
+ },
279
+ body: JSON.stringify({
280
+ key: inspectKey.trim(),
281
+ workspace: selectedWorkspace,
282
+ profile: selectedProfile,
283
+ passphrase: secretPassphrase,
284
+ }),
285
+ });
286
+
287
+ if (!response.ok) {
288
+ throw new Error(await response.text());
289
+ }
290
+
291
+ setInspectPayload((await response.json()) as InspectPayload);
292
+ } catch (err: unknown) {
293
+ setError(err instanceof Error ? err.message : String(err));
294
+ } finally {
295
+ setRevealingInspect(false);
296
+ }
297
+ }
298
+
236
299
  useEffect(() => {
237
300
  void inspectCurrentKey();
238
301
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -293,7 +356,7 @@ export function App() {
293
356
  <div className="text-xs uppercase tracking-[0.26em] text-slate-500">Namespace Browser</div>
294
357
  <h2 className="mt-2 text-2xl font-semibold tracking-[-0.04em] text-slate-950">Effective Config Surface</h2>
295
358
  </div>
296
- <div className="grid gap-3 sm:grid-cols-3">
359
+ <div className="grid gap-3 sm:grid-cols-4">
297
360
  <label className="block">
298
361
  <span className="mb-2 block text-xs uppercase tracking-[0.22em] text-slate-500">Workspace</span>
299
362
  <select
@@ -331,10 +394,20 @@ export function App() {
331
394
  className="w-full rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm outline-none transition focus:border-cyan-400 focus:bg-white sm:w-64"
332
395
  />
333
396
  </label>
397
+ <label className="block">
398
+ <span className="mb-2 block text-xs uppercase tracking-[0.22em] text-slate-500">Vault Passphrase</span>
399
+ <input
400
+ type="password"
401
+ value={secretPassphrase}
402
+ onChange={(event) => setSecretPassphrase(event.target.value)}
403
+ placeholder="Optional"
404
+ className="w-full rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm outline-none transition focus:border-cyan-400 focus:bg-white sm:w-56"
405
+ />
406
+ </label>
334
407
  </div>
335
408
  </div>
336
409
 
337
- <div className="mt-5 flex flex-wrap gap-2">
410
+ <div className="mt-5 flex flex-wrap items-center gap-2">
338
411
  {tabs.map((tab) => (
339
412
  <button
340
413
  key={tab}
@@ -353,6 +426,16 @@ export function App() {
353
426
  {tab}
354
427
  </button>
355
428
  ))}
429
+ {namespace === 'secret' ? (
430
+ <button
431
+ type="button"
432
+ onClick={() => void revealSecretList()}
433
+ disabled={revealingList}
434
+ className="ml-auto rounded-full border border-rose-200 bg-rose-50 px-4 py-2 text-sm font-medium text-rose-800 transition hover:bg-rose-100 disabled:cursor-not-allowed disabled:opacity-60"
435
+ >
436
+ {revealingList ? 'Revealing…' : 'Reveal Secrets'}
437
+ </button>
438
+ ) : null}
356
439
  </div>
357
440
 
358
441
  <div className="mt-5 overflow-hidden rounded-[1.5rem] border border-slate-200">
@@ -410,6 +493,16 @@ export function App() {
410
493
  >
411
494
  Inspect
412
495
  </button>
496
+ {inspectKey.trim().startsWith('secret.') ? (
497
+ <button
498
+ type="button"
499
+ onClick={() => void revealSecretInspect()}
500
+ disabled={revealingInspect}
501
+ className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm font-medium text-rose-800 transition hover:bg-rose-100 disabled:cursor-not-allowed disabled:opacity-60"
502
+ >
503
+ {revealingInspect ? 'Revealing…' : 'Reveal'}
504
+ </button>
505
+ ) : null}
413
506
  </div>
414
507
 
415
508
  <div className="mt-5 rounded-[1.5rem] border border-slate-200 bg-slate-50 p-4">