Vectrum
Sign In
36 lines · 1.6 KB · TypeScript
import "server-only";
import { authenticateRunner, type RunnerIdentity } from "./runners";

/*
 * The HTTP API that runners (public/vectrum-runner.mjs) talk to. Every request carries the runner's
 * token as `Authorization: Bearer vrn_…`.
 *
 *   POST /api/runner/claim       { hostname, os, arch, version }  → 200 { job } or 204 after waiting a while
 *   POST /api/runner/jobs/:id    { steps?, logs?, status? }        → { cancelled }
 */

export const json = (body: unknown, status = 200) => Response.json(body, { status, headers: { "Cache-Control": "no-store" } });

export async function runnerFromRequest(request: Request): Promise<RunnerIdentity | Response> {
  const header = request.headers.get("authorization") ?? "";
  const runner = header.startsWith("Bearer ") ? await authenticateRunner(header.slice(7).trim()) : null;
  if (!runner) return json({ error: "Unknown or expired runner token. Register the runner again in Settings → Runners." }, 401);
  return runner;
}

export async function readBody(request: Request) {
  try {
    const body: unknown = await request.json();
    return typeof body === "object" && body !== null ? (body as Record<string, unknown>) : {};
  } catch {
    return {};
  }
}

/** The server's address as the runner reached it, for clone URLs. */
export function originOf(request: Request) {
  const url = new URL(request.url);
  const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host") ?? url.host;
  const proto = request.headers.get("x-forwarded-proto") ?? url.protocol.replace(":", "");
  return `${proto}://${host}`;
}