72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
// The routes and request body behind linking, listing and deleting an
|
|
// external forge account. Pure, so `npm test` runs them without a browser.
|
|
|
|
/** The kinds that share the label + URL + token shape. */
|
|
export type ForgeLinkKind = "forge" | "gitlab";
|
|
|
|
/** `hive_types::Ident`, which the controller checks for the label. */
|
|
export const LABEL_PATTERN = "[a-z0-9\\-]{1,63}";
|
|
|
|
function agentBase(hive: string, agent: string): string {
|
|
return `/api/hives/${encodeURIComponent(hive)}/agents/${encodeURIComponent(agent)}`;
|
|
}
|
|
|
|
/** PUT target for linking `label` under `kind`. */
|
|
export function linkUrl(
|
|
hive: string,
|
|
agent: string,
|
|
kind: ForgeLinkKind,
|
|
label: string,
|
|
): string {
|
|
const route = kind === "gitlab" ? "gitlab-accounts" : "forge-accounts";
|
|
return `${agentBase(hive, agent)}/${route}/${encodeURIComponent(label)}`;
|
|
}
|
|
|
|
/** The PUT body; the token is sent as typed, the URL trimmed. */
|
|
export function linkBody(url: string, token: string): string {
|
|
return JSON.stringify({ url: url.trim(), token });
|
|
}
|
|
|
|
/** GET target for the agent's linked accounts. */
|
|
export function listUrl(hive: string, agent: string): string {
|
|
return `${agentBase(hive, agent)}/linked-accounts`;
|
|
}
|
|
|
|
/** DELETE target for one linked account, as listed by `listUrl`. */
|
|
export function deleteUrl(
|
|
hive: string,
|
|
agent: string,
|
|
a: { kind: string; name: string },
|
|
): string {
|
|
const base = agentBase(hive, agent);
|
|
const name = encodeURIComponent(a.name);
|
|
switch (a.kind) {
|
|
case "matrix":
|
|
return `${base}/matrix-accounts/${name}`;
|
|
case "forgejo":
|
|
return `${base}/forge-accounts/${name}`;
|
|
case "gitlab":
|
|
return `${base}/gitlab-accounts/${name}`;
|
|
case "github":
|
|
return `${base}/github-account`;
|
|
default:
|
|
return `${base}/backend-env`;
|
|
}
|
|
}
|
|
|
|
/** A message for input the controller would refuse anyway, or null. The
|
|
* controller's own URL rule stays the gate and its 400 is shown as is. */
|
|
export function checkLink(
|
|
label: string,
|
|
url: string,
|
|
token: string,
|
|
): string | null {
|
|
if (!new RegExp(`^${LABEL_PATTERN}$`).test(label)) {
|
|
return "label: 1-63 chars, lowercase letters, digits, hyphen";
|
|
}
|
|
if (!/^https?:\/\/\S+$/.test(url.trim())) {
|
|
return "URL must start with http:// or https://";
|
|
}
|
|
if (token === "") return "token is required";
|
|
return null;
|
|
}
|