1import { Function as NeonFunction } from "@/Neon/Function";
2import { ReadWriteBucket } from "@/Neon/ReadWriteBucket";
3import { ReadWriteBucketHttp } from "@/Neon/ReadWriteBucketHttp";
4import { ReadObject } from "@/Neon/ReadObject";
5import { ReadObjectHttp } from "@/Neon/ReadObjectHttp";
6import { WriteObject } from "@/Neon/WriteObject";
7import { WriteObjectHttp } from "@/Neon/WriteObjectHttp";
8import * as Effect from "effect/Effect";
9import * as Layer from "effect/Layer";
10import * as HttpServerResponse from "effect/http/HttpServerResponse";
11import { HttpServerRequest } from "effect/http/HttpServerRequest";
12import {
13 StorageBranch,
14 StorageBucket,
15 StorageSettings,
16} from "./StorageResources.ts";
17
18export default class StorageFunction extends NeonFunction<StorageFunction>()(
19 "StorageFunction",
20 Effect.gen(function* () {
21 return { branch: yield* StorageBranch, main: import.meta.url };
22 }),
23 Effect.gen(function* () {
24 const bucket = yield* StorageBucket;
25 const object = yield* StorageSettings;
26 const files = yield* ReadWriteBucket(bucket);
27 const settings = yield* ReadObject(object);
28 const writer = yield* WriteObject(object);
29 return {
30 fetch: Effect.gen(function* () {
31 const request = yield* HttpServerRequest;
32 if (request.url === "/presign") {
33 return yield* HttpServerResponse.json({
34 url: yield* files.presignPut("signed/spaces & unicode-λ.txt", {
35 contentType: "text/plain",
36 expiresIn: 60,
37 }),
38 });
39 }
40 if (request.url === "/download") {
41 return yield* HttpServerResponse.json({
42 url: yield* files.presignGet("signed/spaces & unicode-λ.txt", {
43 expiresIn: 60,
44 }),
45 });
46 }
47 if (request.url === "/write") {
48 return yield* writer.put({ theme: "dark", pageSize: 50 }).pipe(
49 Effect.as(HttpServerResponse.text("ok")),
50 Effect.catch((error) =>
51 Effect.succeed(
52 HttpServerResponse.text("Storage write failed", {
53 status: 500,
54 headers: { "x-storage-error": error._tag },
55 }),
56 ),
57 ),
58 );
59 }
60 if (request.url === "/invalid") {
61 yield* files.put(
62 "settings.json",
63 '{"theme":"dark","pageSize":"bad"}',
64 { ContentType: "application/json" },
65 );
66 return yield* settings.get().pipe(
67 Effect.as(HttpServerResponse.text("accepted", { status: 500 })),
68 Effect.catchTag("ObjectDecodeError", () =>
69 Effect.succeed(HttpServerResponse.text("rejected")),
70 ),
71 );
72 }
73 return yield* HttpServerResponse.json(yield* settings.get());
74 }).pipe(
75 Effect.catch(() =>
76 Effect.succeed(
77 HttpServerResponse.text("Storage request failed", { status: 500 }),
78 ),
79 ),
80 ),
81 };
82 }).pipe(
83 Effect.provide(
84 Layer.mergeAll(ReadWriteBucketHttp, ReadObjectHttp, WriteObjectHttp),
85 ),
86 ),
87) {}