1import * as Cloudflare from "alchemy/Cloudflare";
2import * as Effect from "effect/Effect";
3import * as Schema from "effect/Schema";
4import { HttpServerRequest } from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6import Users from "./Users.ts";
7
8const CreateUser = Schema.Struct({ name: Schema.String });
9
10export default class Api extends Cloudflare.Worker<Api>()(
11 "Api",
12 { main: import.meta.url },
13 Effect.gen(function* () {
14 const users = yield* Users;
15
16 return {
17 fetch: Effect.gen(function* () {
18 const request = yield* HttpServerRequest;
19 const path = request.url.split("?")[0];
20 const match = /^\/objects\/([a-zA-Z0-9_-]+)\/users$/.exec(path);
21 if (!match) {
22 return yield* HttpServerResponse.json(
23 { error: "Use /objects/:name/users (letters, digits, _ or -)" },
24 { status: 404 },
25 );
26 }
27
28 const object = users.getByName(match[1]);
29 if (request.method === "GET") {
30 const rows = yield* object.listUsers();
31 return yield* HttpServerResponse.json({ users: rows });
32 }
33 if (request.method === "POST") {
34 const body = yield* request.json.pipe(
35 Effect.flatMap(Schema.decodeUnknownEffect(CreateUser)),
36 Effect.catch(() => Effect.succeed(undefined)),
37 );
38 if (!body?.name.trim()) {
39 return yield* HttpServerResponse.json(
40 { error: "Expected JSON with a non-empty name" },
41 { status: 400 },
42 );
43 }
44 const [user] = yield* object.addUser(body.name.trim());
45 return yield* HttpServerResponse.json({ user }, { status: 201 });
46 }
47 return yield* HttpServerResponse.json(
48 { error: "Method not allowed" },
49 { status: 405, headers: { allow: "GET, POST" } },
50 );
51 }).pipe(
52 Effect.catchTag("EffectDrizzleQueryError", () =>
53 HttpServerResponse.json(
54 { error: "Database query failed" },
55 { status: 500 },
56 ),
57 ),
58 ),
59 };
60 }),
61) {}