Skip to content
52 linesCodeBlameRaw
1/**
2 * The desktop app's runner, from its page (lib/desktop-runner.ts): a
3 * registration token for a workspace the person owns, which the page
4 * hands to the app and never shows, and the app's reports of how its
5 * runner is, kept for the workspace's Runners page.
6 *
7 * Signed in, same origin, and owners of the workspace only, as the
8 * Runners page's own New runner is; the actions service checks again.
9 * `-` is no workspace's name, so nothing else is ever found here.
10 *
11 * POST { "intent": "token", "workspace": "acme" }
12 * → { "token": "g1trt_…", "workspace": "acme", "url": "https://g1t.sh", "expires_at": "…" }
13 * → 403 { "error": "…", "allowed": false }
14 * POST { "intent": "report", "workspace", "machine", "backend", "version", "diagnosis", "closed" }
15 * → { "ok": true }
16 */
17import type { Route } from "./+types/desktop-runner";
18import { backendOf, diagnosisOf, text } from "../lib/desktop-runner";
19import { runners } from "../lib/runners.server";
20import { assertSameOrigin, getViewer, roleIn } from "../lib/session.server";
21
22export async function action({ request, context }: Route.ActionArgs) {
23 assertSameOrigin(request);
24 const headers = { "cache-control": "no-store" };
25 const viewer = getViewer(context);
26 if (!viewer || (viewer.kind ?? "user") !== "user") return Response.json({ error: "Sign in to g1t." }, { status: 401, headers });
27 const body = (await request.json().catch(() => null)) as Record<string, unknown> | null;
28 const workspace = text(body?.workspace)?.toLowerCase() ?? null;
29 if (!body || !workspace) return Response.json({ error: "Name a workspace." }, { status: 400, headers });
30 if (roleIn(viewer, workspace) !== "owner") {
31 return Response.json({ error: `You can't add runners to ${workspace}: only its owners can.`, allowed: false }, { status: 403, headers });
32 }
33 switch (body.intent) {
34 case "token": {
35 const made = await runners.createToken(viewer, { workspace });
36 if (!made.ok) return Response.json({ error: made.error.message, allowed: true }, { status: 409, headers });
37 const token = made.value;
38 return Response.json({ token: token.token, workspace: token.workspace, url: token.url, expires_at: token.expiresAt }, { headers });
39 }
40 case "report": {
41 const backend = backendOf(body.backend);
42 const machine = text(body.machine);
43 const diagnosis = diagnosisOf(body.diagnosis);
44 const closed = diagnosisOf(body.closed);
45 if (!backend || !machine || !diagnosis || !closed) return Response.json({ error: "That isn't a desktop's report." }, { status: 400, headers });
46 const kept = await runners.reportDesktop(viewer, { workspace, machine, backend, version: text(body.version), diagnosis, closed });
47 return kept.ok ? Response.json({ ok: true }, { headers }) : Response.json({ error: kept.error.message }, { status: 409, headers });
48 }
49 default:
50 return Response.json({ error: "Nothing to do." }, { status: 400, headers });
51 }
52}