1import * as Fly from "@/Fly";
2import * as Config from "effect/Config";
3import * as Effect from "effect/Effect";
4import * as Redacted from "effect/Redacted";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7
8export const Site = Fly.App("Site");
9export const CacheOne = Fly.Redis("CacheOne");
10export const CacheTwo = Fly.Redis("CacheTwo");
11
12export class BoundSecrets extends Fly.Service<BoundSecrets>()(
13 "BoundSecrets",
14 {
15 app: Site,
16 main: import.meta.url,
17 count: 2,
18 port: 3000,
19 deploy: { strategy: "bluegreen", healthTimeout: "60 seconds" },
20 shutdown: { timeout: "10 seconds" },
21 services: [
22 {
23 protocol: "tcp",
24 internalPort: 3000,
25 autostop: "off",
26 ports: [{ port: 80, handlers: ["http"] }],
27 checks: [
28 {
29 type: "http",
30 port: 3000,
31 path: "/ready",
32 interval: "2s",
33 timeout: "1s",
34 },
35 ],
36 },
37 ],
38 },
39 Effect.gen(function* () {
40 const token = yield* Config.Redacted("ACCEPTANCE_BOUND_SECRET");
41 const slot = yield* Config.String("ACCEPTANCE_CACHE");
42 const cache = yield* Fly.ReadWriteRedis(
43 slot === "one" ? CacheOne : CacheTwo,
44 );
45 const version = Redacted.value(token).endsWith("-one") ? "one" : "two";
46 return {
47 fetch: Effect.gen(function* () {
48 const request = yield* HttpServerRequest;
49 if (request.url.startsWith("/ready")) {
50 yield* cache.ping().pipe(Effect.orDie);
51 return HttpServerResponse.text("ready");
52 }
53 if (request.url.startsWith("/seed")) {
54 yield* cache.set("bluegreen-marker", version).pipe(Effect.orDie);
55 }
56 const value = yield* cache.get("bluegreen-marker").pipe(Effect.orDie);
57 return yield* HttpServerResponse.json({
58 config: version,
59 redis: value === "one" ? "one" : value === "two" ? "two" : "empty",
60 });
61 }),
62 };
63 }).pipe(Effect.provide(Fly.ReadWriteRedisHttp)),
64) {}
65
66export default BoundSecrets;