import { useState } from "react"; import { Link } from "react-router"; import { useSWRConfig } from "swr"; import { PlusIcon } from "@heroicons/react/16/solid"; import type { SecretMetadata } from "@qltysh/fabro-api-client"; import { ApiError, apiData, secretsApi } from "../lib/api-client"; import { useSecrets } from "../lib/queries"; import { queryKeys } from "../lib/query-keys"; import { Badge, Panel, PanelSkeleton, SettingsPageIntro, } from "../components/settings-panel"; import { COMPACT_SECONDARY_BUTTON_CLASS, ConfirmDialog } from "../components/ui"; import { useToast } from "../components/toast"; import { formatAbsoluteTs, formatRelativeTime } from "../lib/format"; export function meta() { return [{ title: "Secrets — Fabro" }]; } const DESCRIPTION = "Secrets are values stored on this Fabro server and made available to workflow runs. Values are write-only — they can be replaced or deleted, but never read back through the UI."; export default function SettingsSecrets() { const query = useSecrets(); return (
); } function SecretsPanel({ secrets }: { secrets: SecretMetadata[] }) { const { mutate } = useSWRConfig(); const toast = useToast(); const [pendingDeleteName, setPendingDeleteName] = useState(null); const [deleting, setDeleting] = useState(false); async function confirmDelete() { if (!pendingDeleteName) return; const name = pendingDeleteName; setDeleting(true); try { await apiData(() => secretsApi.deleteSecretByName({ name })); await mutate(queryKeys.secrets.list()); toast.push({ message: `Secret “${name}” deleted.` }); setPendingDeleteName(null); } catch (cause) { toast.push({ tone: "error", message: cause instanceof ApiError && cause.message ? cause.message : "Couldn't delete the secret. Please try again.", }); } finally { setDeleting(false); } } return ( <> {secrets.length === 0 ? (
No secrets stored yet.
) : ( secrets.map((secret) => ( setPendingDeleteName(secret.name)} /> )) )}
Delete{" "} {pendingDeleteName}? Workflow runs that depend on it will no longer have access. } confirmLabel="Delete" pendingLabel="Deleting…" pending={deleting} onConfirm={confirmDelete} onCancel={() => { if (!deleting) setPendingDeleteName(null); }} /> ); } function SecretRow({ secret, disabled, onDelete, }: { secret: SecretMetadata; disabled: boolean; onDelete: () => void; }) { return (
{secret.name} {secret.type}
{secret.description ? {secret.description} · : null} Updated {formatRelativeTime(secret.updated_at)}
); }