Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: show and toggle an agent's unrestricted internet flag, add it to the create dialog

This commit is contained in:
atlas 2026-10-27 13:38:58 +01:00 • committed by mara
commit 10508895c4
2 changed files with 136 additions and 2 deletions

View file

@ -23,12 +23,18 @@ import {
requestPermissions,
togglePermission,
} from "../../pages/CreateAgentPermissions.js";
import { editorSelection, grantSummary } from "./permissions.js";
import {
editorSelection,
grantSummary,
internetBody,
internetSummary,
} from "./permissions.js";
import "./AgentLimits.css";
// The stored grant as the route carries it; the field is absent when unset.
interface Grant {
permissions?: string[];
unrestricted_internet?: boolean;
}
// Mirrors swarm-controller's `Catalogue` (`GET /api/permissions`).
@ -47,6 +53,7 @@ export function AgentPermissions({ agent }: { agent: string }) {
// Bumped after a save or a clear, so the grant is fetched again.
const [reload, setReload] = useState(0);
const [editing, setEditing] = useState(false);
const [flagBusy, setFlagBusy] = useState(false);
useEffect(() => {
let cancelled = false;
@ -68,6 +75,28 @@ export function AgentPermissions({ agent }: { agent: string }) {
};
}, [agent, reload]);
// Flips the internet flag, sending the stored list with it.
async function setInternet(g: Grant, unrestricted: boolean) {
if (flagBusy) return;
setFlagBusy(true);
try {
const r = await fetch(permissionsUrl(agent), {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify(internetBody(g.permissions, unrestricted)),
});
if (!r.ok) {
setError(await readApiError(r));
return;
}
setReload((n) => n + 1);
} catch (e: unknown) {
setError({ detail: String(e) });
} finally {
setFlagBusy(false);
}
}
// A grant already loaded stays under a failed refetch, so a save that
// worked is not hidden by a read that did not.
const errorPanel = error ? (
@ -99,6 +128,22 @@ export function AgentPermissions({ agent }: { agent: string }) {
))}
</ul>
) : null}
<Badge
label="internet"
value={internetSummary(grant.unrestricted_internet)}
/>
<Badge
variant="quiet"
value={
grant.unrestricted_internet === false
? "allow unrestricted"
: "restrict"
}
title={`set whether ${agent}'s traffic may leave the host unfiltered`}
onClick={() =>
void setInternet(grant, grant.unrestricted_internet === false)
}
/>
<Badge
variant="quiet"
value="edit"

View file

@ -21,3 +21,27 @@ export function grantSummary(stored: string[] | undefined): string {
if (stored.length === 0) return "none";
return `${stored.length} granted`;
}
/**
* How the panel reads the stored `unrestricted_internet`. An absent value
* counts as on, and says "legacy" because the swarm never set it.
*/
export function internetSummary(stored: boolean | undefined): string {
if (stored === undefined) return "unrestricted (legacy)";
return stored ? "unrestricted" : "restricted";
}
/**
* The `PUT` body that sets the internet flag. A `PUT` without `permissions`
* resets a stored list to the default, so the stored list goes along; an
* agent with none stored sends the flag alone, which leaves it unset.
*/
export function internetBody(
stored: string[] | undefined,
unrestricted: boolean,
): { permissions?: string[]; unrestricted_internet: boolean } {
return {
...(stored === undefined ? {} : { permissions: stored }),
unrestricted_internet: unrestricted,
};
}