feat(app): Secrets management page + backend-aware API

- new Secrets page (/secrets, nav entry): lists vault secrets, reveal/copy,
  add/edit/delete, shows active backend + addr + role; read-only on followers
- GET /secrets/info (backend/addr/role/writable); fix /secrets CRUD to use the
  castle.yaml-selected backend (was defaulting to the now-empty file backend)
- OpenBaoBackend.list_names drops folder entries (node-prefix groups)
- tests: conftest forces file backend so host castle.yaml can't leak into tests

app builds clean (tsc 0); endpoints verified live against the vault.
This commit is contained in:
2026-07-07 06:53:16 -07:00
parent c525e154da
commit 5949543ace
8 changed files with 310 additions and 4 deletions

View File

@@ -543,3 +543,43 @@ export function useEventStream() {
return () => es.close()
}, [qc])
}
// --- Secrets ---
export interface SecretsInfo {
backend: string
addr: string | null
role: string
writable: boolean
}
export function useSecretsInfo() {
return useQuery({
queryKey: ["secrets-info"],
queryFn: () => apiClient.get<SecretsInfo>("/secrets/info"),
})
}
export function useSecrets() {
return useQuery({
queryKey: ["secrets"],
queryFn: () => apiClient.get<string[]>("/secrets"),
})
}
export function useSetSecret() {
const qc = useQueryClient()
return useMutation({
mutationFn: ({ name, value }: { name: string; value: string }) =>
apiClient.put(`/secrets/${name}`, { value }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["secrets"] }),
})
}
export function useDeleteSecret() {
const qc = useQueryClient()
return useMutation({
mutationFn: (name: string) => apiClient.delete(`/secrets/${name}`),
onSuccess: () => qc.invalidateQueries({ queryKey: ["secrets"] }),
})
}