import { useState } from "react"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import { LuEye, LuEyeOff, LuPencil, LuPlus, LuTrash2 } from "react-icons/lu"; import { errorMessage } from "../api/client.js"; import { fetchHttpAuthLiterals, useDeleteHttpAuth, useHttpAuths, } from "../api/hooks.js"; import { AuthEditorModal } from "../components/AuthEditorModal.jsx"; import { ConfirmDialog } from "../components/ConfirmDialog.jsx"; import { useRouteDrivenModal } from "../hooks/useRouteDrivenModal.js"; import { formatTime } from "../lib/format.jsx"; function CredDisplay({ field, fieldKey, authId, cache, onRevealed }) { const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); if (!field || field.source === "missing") { return —; } if (field.source === "secret") { return ( secret:{field.secret} ); } if (field.source === "kv") { const ref = field.namespace ? `kv:${field.namespace}/${field.kv}` : `kv:${field.kv}`; return {ref}; } const revealed = cache?.[fieldKey]; const shown = open && typeof revealed === "string"; async function toggle() { if (open) { setOpen(false); return; } if (typeof revealed === "string") { setOpen(true); return; } setLoading(true); setError(null); try { const data = await fetchHttpAuthLiterals(authId); onRevealed?.(data.literals ?? {}); setOpen(true); } catch (err) { setError(errorMessage(err)); } finally { setLoading(false); } } return ( {shown ? revealed : "***"} {error ? {error} : null} ); } function CredentialsCell({ auth, cache, onRevealed }) { const cfg = auth.config ?? {}; if (auth.type === "bearer") { return ( ); } if (auth.type === "basic") { return ( user pass ); } return ( {cfg.header ?? "?"} ); } export function AuthProfilesPage() { const navigate = useNavigate(); const location = useLocation(); const { name: routeName } = useParams(); const isNewRoute = /\/auth\/new\/?$/.test(location.pathname); const isEditRoute = Boolean(routeName) && !isNewRoute; const { data: auths = [], isLoading } = useHttpAuths(); const del = useDeleteHttpAuth(); const [confirmDelete, setConfirmDelete] = useState(null); /** @type {[Record>, Function]} */ const [revealCache, setRevealCache] = useState({}); const { editor, closeEditor } = useRouteDrivenModal({ isNewRoute, isEditRoute, listPath: "/auth", buildNewEditor: () => ({ mode: "add" }), editRouteKey: () => `edit:${routeName}`, canOpenEdit: () => !isLoading, buildEditEditor: () => { const auth = auths.find((a) => a.name === routeName); if (!auth) return { mode: "add" }; return { mode: "edit", auth }; }, editDeps: [routeName, isLoading, auths], }); return (

Auth

HTTP trigger auth profiles. Rename freely — workflows keep working via a stable id. Add them to a trigger from the workflow editor dropdown. Secrets are managed on the Secrets page; KV values stay in KV.

{isLoading ? ( ) : auths.length === 0 ? (

No auth profiles yet.

) : (
{auths.map((a) => ( ))}
Name Type Credentials Updated
{a.name} {a.type} setRevealCache((prev) => ({ ...prev, [a.id]: literals })) } /> {formatTime(a.updated_at)}
)} {editor ? ( { const id = saved?.id || editor.auth?.id; if (!id) return; setRevealCache((prev) => { const next = { ...prev }; delete next[id]; return next; }); }} /> ) : null} setConfirmDelete(null)} onConfirm={() => del.mutate(confirmDelete.id, { onSuccess: () => setConfirmDelete(null) }) } />
); }