d6be46d313
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
36 lines
2.3 KiB
TypeScript
36 lines
2.3 KiB
TypeScript
import type { TaskTreeCommand } from "./contracts.ts";
|
|
|
|
export function createTaskTreeHttpApp(options: { dispatch: (command: TaskTreeCommand) => Promise<any>; close?: () => Promise<void> }) {
|
|
return {
|
|
async fetch(request: Request): Promise<Response> {
|
|
const url = new URL(request.url);
|
|
if (url.pathname === "/health/live") return json(200, { ok: true, serviceId: "hwlab-tasktree-api", status: "live" });
|
|
if (url.pathname === "/health/ready") return resultResponse(await options.dispatch({ operation: "health" }));
|
|
if (url.pathname === "/v1/tasktree/groups" && request.method === "GET") return resultResponse(await options.dispatch({ operation: "group.list" }));
|
|
if (url.pathname === "/v1/tasktree/groups" && request.method === "POST") {
|
|
const body = await bodyObject(request);
|
|
return resultResponse(await options.dispatch({ operation: "group.create", name: text(body.name), description: text(body.description) }), 201);
|
|
}
|
|
const timeline = /^\/v1\/tasktree\/groups\/([^/]+)\/timeline$/u.exec(url.pathname);
|
|
if (timeline && request.method === "GET") return resultResponse(await options.dispatch({ operation: "timeline.get", groupId: decodeURIComponent(timeline[1]) }));
|
|
if (url.pathname === "/v1/tasktree/commands" && request.method === "POST") {
|
|
return resultResponse(await options.dispatch(await bodyObject(request) as TaskTreeCommand));
|
|
}
|
|
return json(404, { ok: false, error: { code: "not_found", message: "TaskTree route was not found" } });
|
|
},
|
|
close: options.close ?? (async () => {})
|
|
};
|
|
}
|
|
|
|
function resultResponse(result: any, successStatus = 200) {
|
|
const status = result?.ok === true ? successStatus : result?.error?.code?.includes("not_found") ? 404 : 400;
|
|
return json(status, result);
|
|
}
|
|
async function bodyObject(request: Request): Promise<Record<string, any>> {
|
|
const body = await request.json().catch(() => null);
|
|
if (!body || typeof body !== "object" || Array.isArray(body)) throw Object.assign(new Error("JSON object body is required"), { code: "invalid_json" });
|
|
return body as Record<string, any>;
|
|
}
|
|
function text(value: unknown) { return String(value ?? ""); }
|
|
function json(status: number, body: unknown) { return Response.json(body, { status, headers: { "cache-control": "no-store" } }); }
|