1import * as Fly from "@/Fly";
2import * as Effect from "effect/Effect";
3import * as FileSystem from "effect/FileSystem";
4import { HttpServerRequest } from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6import { API_PORT, MARKER, MARKER_FILE, Site, VOLUME_PATH } from "./shared.ts";
7
8/**
9 * HTTP Service: mounts a per-replica disk, writes a marker, and serves it back.
10 */
11export default class Api extends Fly.Service<Api>()(
12 "Api",
13 {
14 app: Site,
15 main: import.meta.url,
16 region: "iad",
17 port: API_PORT,
18 guest: { cpuKind: "shared", cpus: 1, memoryMb: 256 },
19 },
20 Effect.gen(function* () {
21 const mount = yield* Fly.MountVolume({ path: VOLUME_PATH, sizeGb: 1 });
22 const fs = yield* FileSystem.FileSystem;
23
24 return {
25 fetch: Effect.gen(function* () {
26 const request = yield* HttpServerRequest;
27 const url = new URL(request.url, "http://service");
28 if (url.pathname === "/health") {
29 return yield* HttpServerResponse.json({
30 ok: true,
31 path: mount.path,
32 });
33 }
34 yield* fs
35 .makeDirectory(mount.path, { recursive: true })
36 .pipe(Effect.orDie);
37 yield* fs.writeFileString(MARKER_FILE, MARKER).pipe(Effect.orDie);
38 const text = yield* fs.readFileString(MARKER_FILE).pipe(Effect.orDie);
39 return yield* HttpServerResponse.json({
40 path: mount.path,
41 text,
42 });
43 }),
44 };
45 }).pipe(Effect.provide(Fly.MountVolumeLive)),
46) {}