1import * as Railway from "@/Railway";
2import * as Effect from "effect/Effect";
3import { HttpServerRequest } from "effect/http/HttpServerRequest";
4import * as HttpServerResponse from "effect/http/HttpServerResponse";
5import { Partition } from "./suite-env.ts";
6import { Cache, REDIS_KEY, REDIS_VALUE, Site } from "./redis-shared.ts";
7
8export { Cache, REDIS_KEY, REDIS_VALUE, Site };
9
10export const REDIS_PORT = 3000;
11
12/**
13 * HTTP Service that PINGs Redis and round-trips a key via
14 * {@link Railway.ReadWriteRedis}.
15 */
16export default class RedisApi extends Railway.Service<RedisApi>()(
17 "RedisApi",
18 {
19 project: Site,
20 environment: Partition,
21 main: import.meta.url,
22 port: REDIS_PORT,
23 },
24 Effect.gen(function* () {
25 const cache = yield* Railway.ReadWriteRedis(Cache);
26
27 return {
28 fetch: Effect.gen(function* () {
29 const request = yield* HttpServerRequest;
30 const path = new URL(request.url, "http://service").pathname;
31
32 if (path === "/health" || path === "/") {
33 const body = yield* cache.ping();
34 return yield* HttpServerResponse.json({ pong: /pong/i.test(body) });
35 }
36
37 if (path === "/set") {
38 yield* cache.set(REDIS_KEY, REDIS_VALUE);
39 return yield* HttpServerResponse.json({ ok: true, key: REDIS_KEY });
40 }
41
42 if (path === "/get") {
43 const value = yield* cache.get(REDIS_KEY);
44 return yield* HttpServerResponse.json({
45 ok: value === REDIS_VALUE,
46 value,
47 });
48 }
49
50 return yield* HttpServerResponse.json({ ok: false }, { status: 404 });
51 }).pipe(
52 Effect.catch((error) =>
53 HttpServerResponse.json(
54 {
55 ok: false,
56 error: String(error),
57 cause:
58 error !== null && typeof error === "object" && "cause" in error
59 ? String((error as { cause: unknown }).cause)
60 : undefined,
61 },
62 { status: 500 },
63 ),
64 ),
65 ),
66 };
67 }).pipe(Effect.provide(Railway.ReadWriteRedisHttp)),
68) {}