1import * as Railway from "@/Railway";
2import * as Effect from "effect/Effect";
3import * as Stream from "effect/Stream";
4import { HttpServerRequest } from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6import { Partition, Site } from "./suite-env.ts";
7
8export { Site };
9
10export const BUCKET_PORT = 3000;
11export const OBJECT_KEY = "alchemy-marker.txt";
12export const OBJECT_BODY = "hello-from-railway";
13
14export const Data = Railway.Bucket("Data", {
15 project: Site,
16 environment: Partition,
17});
18
19/**
20 * HTTP Service that puts and gets an object on a Railway bucket via
21 * the S3 API.
22 */
23export default class BucketApi extends Railway.Service<BucketApi>()(
24 "BucketApi",
25 Effect.gen(function* () {
26 const bucket = yield* Data;
27 return {
28 project: Site,
29 environment: Partition,
30 main: import.meta.url,
31 port: BUCKET_PORT,
32 env: {
33 BUCKET_NAME: bucket.s3BucketName,
34 AWS_ACCESS_KEY_ID: bucket.accessKeyId,
35 AWS_SECRET_ACCESS_KEY: bucket.secretAccessKey,
36 AWS_ENDPOINT_URL_S3: bucket.endpoint,
37 AWS_ENDPOINT_URL: bucket.endpoint,
38 AWS_REGION: bucket.s3Region,
39 },
40 };
41 }),
42 Effect.gen(function* () {
43 const putObject = yield* Railway.PutObject(Data);
44 const getObject = yield* Railway.GetObject(Data);
45
46 return {
47 fetch: Effect.gen(function* () {
48 const request = yield* HttpServerRequest;
49 const path = new URL(request.url, "http://service").pathname;
50
51 if (path === "/health") {
52 return yield* HttpServerResponse.json({ ok: true });
53 }
54
55 if (path === "/put") {
56 yield* putObject({
57 Key: OBJECT_KEY,
58 Body: OBJECT_BODY,
59 ContentType: "text/plain",
60 });
61 return yield* HttpServerResponse.json({ ok: true, key: OBJECT_KEY });
62 }
63
64 if (path === "/get") {
65 const obj = yield* getObject({ Key: OBJECT_KEY });
66 const text =
67 obj.Body === undefined
68 ? ""
69 : yield* Stream.mkString(Stream.decodeText(obj.Body));
70 return yield* HttpServerResponse.json({
71 ok: text === OBJECT_BODY,
72 text,
73 });
74 }
75
76 return yield* HttpServerResponse.json({ ok: false }, { status: 404 });
77 }).pipe(
78 Effect.catch((error) =>
79 HttpServerResponse.json(
80 { ok: false, error: String(error) },
81 { status: 500 },
82 ),
83 ),
84 ),
85 };
86 }).pipe(Effect.provide([Railway.PutObjectHttp, Railway.GetObjectHttp])),
87) {}