Fork Horse repository, listed by its owner. Publisher snapshot.

packages/forkhorse-core/src/api.ts

Snapshot at 34e3175

Containing directory

2,741 bytes · typescriptView raw
1import { body, errorResponse, HttpError, json, operation, slug } from "./http";
2import type { Env } from "./types";
3
4export async function dispatchApi(request: Request, env: Env, owner = false): Promise<Response> {
5  try {
6    const url = new URL(request.url);
7    let project: string;
8    if (request.method === "POST" && url.pathname === "/projects") {
9      const data = await body(request);
10      if (!slug(data.id))
11        throw new HttpError(400, "id must be a lowercase slug of at most 48 characters.");
12      project = data.id;
13      request = new Request(request.url, {
14        method: request.method,
15        headers: request.headers,
16        body: JSON.stringify(data),
17      });
18    } else {
19      const match = /^\/projects\/([^/]+)(\/.*)$/.exec(url.pathname);
20      if (!match || !slug(match[1])) return json({ error: "Not found." }, 404);
21      const suffix = match[2]!;
22      const methods =
23        suffix === "/settings" ? ["PATCH"]
24        : /^\/reviews\/[^/]+(?:\/diff)?$/.test(suffix) ? ["GET"]
25        : /^\/reviews\/[^/]+\/receipt$/.test(suffix) ? ["GET"]
26        : suffix === "/join" ||
27        suffix === "/agents" ||
28        suffix === "/reviews" ||
29        suffix === "/usage" ||
30        /^\/races\/[^/]+\/verdict$/.test(suffix) ||
31        /^\/agents\/[^/]+\/rotate$/.test(suffix) ||
32        /^\/forks\/[^/]+\/(commit|token)$/.test(suffix) ||
33        /^\/packets\/[^/]+\/(archive|unarchive)$/.test(suffix) ||
34        /^\/blockers\/[^/]+\/resolve$/.test(suffix) ||
35        /^\/reviews\/[^/]+\/(approve|promote|return)$/.test(suffix)
36          ? ["POST"]
37          : /^\/forks\/[^/]+\/tokens$/.test(suffix)
38            ? ["DELETE"]
39            : /^\/packets\/[^/]+\/claim$/.test(suffix)
40              ? ["POST", "DELETE"]
41              : suffix === "/packets"
42                ? ["GET", "POST"]
43                : ["/activity", "/blockers", "/state", "/me"].includes(suffix)
44                  ? ["GET"]
45                  : [];
46      if (!methods.length) return json({ error: "Not found." }, 404);
47      if (!methods.includes(request.method)) return json({ error: "Method not allowed." }, 405);
48      project = match[1];
49    }
50    const supervisor = env.SUPERVISOR.get(env.SUPERVISOR.idFromName(project));
51    if (!owner) return await supervisor.fetch(request);
52    const reply = await supervisor.ownerRequest({
53      url: request.url,
54      method: request.method,
55      body: request.body ? JSON.stringify(await body(request)) : undefined,
56    });
57    return new Response(reply.body, {
58      status: reply.status,
59      headers: {
60        "content-type": "application/json",
61        "cache-control": "no-store",
62      },
63    });
64  } catch (error) {
65    return errorResponse(error, { op: operation(request) });
66  }
67}
68