Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/agent/detail/useAllowedDomains.ts

84 lines
2.5 KiB
TypeScript

// The swarm's allowed-domain and denied-domain entries and the one change that
// is not a form: removing an entry. Shared by an agent card's allowed domains
// and the swarm page's egress card, which differ only in which entries they
// show.
//
// Reads `GET <url>` (every entry) and removes one with a `DELETE` carrying the
// entry, each answering the whole list. `refetch` reads the list again, for a
// caller that added an entry through its own form. The deny list also reports
// `behind`, the hives that have not taken its current revision yet.
import { useEffect, useState } from "preact/hooks";
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import {
ALLOWED_DOMAINS_URL,
DENIED_DOMAINS_URL,
type AllowedDomain,
} from "./allowed-domains.js";
interface Listing {
entries: AllowedDomain[];
behind?: string[];
}
function useDomainList(url: string) {
const [entries, setEntries] = useState<AllowedDomain[] | null>(null);
const [behind, setBehind] = useState<string[]>([]);
const [error, setError] = useState<ProblemDetails | null>(null);
// Bumped after an add or a removal, so the list is fetched again.
const [reload, setReload] = useState(0);
const [busy, setBusy] = useState(false);
useEffect(() => {
let cancelled = false;
(async () => {
const r = await fetch(url);
if (!r.ok) {
if (!cancelled) setError(await readApiError(r));
return;
}
const body = (await r.json()) as Listing;
if (cancelled) return;
setEntries(body.entries);
setBehind(body.behind ?? []);
setError(null);
})().catch((e: unknown) => {
if (!cancelled) setError({ detail: String(e) });
});
return () => {
cancelled = true;
};
}, [url, reload]);
async function remove(entry: AllowedDomain) {
if (busy) return;
setBusy(true);
try {
const r = await fetch(url, {
method: "DELETE",
headers: { "content-type": "application/json" },
body: JSON.stringify(entry),
});
if (!r.ok) {
setError(await readApiError(r));
return;
}
setReload((n) => n + 1);
} catch (e: unknown) {
setError({ detail: String(e) });
} finally {
setBusy(false);
}
}
return {
entries,
behind,
error,
busy,
remove,
refetch: () => setReload((n) => n + 1),
};
}
export const useAllowedDomains = () => useDomainList(ALLOWED_DOMAINS_URL);
export const useDeniedDomains = () => useDomainList(DENIED_DOMAINS_URL);