1import * as Cloudflare from "alchemy/Cloudflare";
2import * as Effect from "effect/Effect";
3import { HttpServerRequest } from "effect/http/HttpServerRequest";
4import * as HttpServerResponse from "effect/http/HttpServerResponse";
5import { Photos, Sessions } from "./resources.ts";
6
7export default Cloudflare.Worker(
8 "Api",
9 { main: import.meta.url },
10 Effect.gen(function* () {
11 const photos = yield* Cloudflare.R2.ReadWriteBucket(Photos);
12 const sessions = yield* Cloudflare.KV.ReadWriteNamespace(Sessions);
13
14 return {
15 fetch: Effect.gen(function* () {
16 const request = yield* HttpServerRequest;
17 const path = new URL(request.url, "http://localhost").pathname;
18
19 // GET /photos — the gallery: every object key in Photos
20 if (path === "/photos" && request.method === "GET") {
21 const list = yield* photos.list();
22 return yield* HttpServerResponse.json({
23 photos: list.objects.map((object) => object.key),
24 });
25 }
26
27 // PUT /photos/:key — upload the request body to Photos
28 if (path.startsWith("/photos/") && request.method === "PUT") {
29 const key = path.slice("/photos/".length);
30 yield* photos.put(key, request.stream, {
31 contentLength: Number(request.headers["content-length"] ?? 0),
32 });
33 return HttpServerResponse.empty({ status: 201 });
34 }
35
36 // GET /session — with `x-session-id`, read the session back from
37 // Sessions; without it, start a new session and store it
38 if (path === "/session" && request.method === "GET") {
39 const id = request.headers["x-session-id"];
40 if (id) {
41 const value = yield* sessions.get(`session:${id}`);
42 return yield* HttpServerResponse.json({ id, value });
43 }
44 const newId = yield* Effect.sync(() => crypto.randomUUID());
45 yield* sessions.put(`session:${newId}`, newId);
46 return yield* HttpServerResponse.json({ id: newId, value: newId });
47 }
48
49 return HttpServerResponse.text("Not found", { status: 404 });
50 }).pipe(
51 Effect.catchTag(["R2Error", "NamespaceError"], (error) =>
52 Effect.succeed(
53 HttpServerResponse.text(error.message, { status: 500 }),
54 ),
55 ),
56 ),
57 };
58 }).pipe(
59 Effect.provide(Cloudflare.R2.ReadWriteBucketBinding),
60 Effect.provide(Cloudflare.KV.ReadWriteNamespaceBinding),
61 ),
62);