1import * as Drizzle from "@/Drizzle/MySQL.ts";
2import * as Railway from "@/Railway";
3import * as Effect from "effect/Effect";
4import { HttpServerRequest } from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6import { Partition, Site } from "./suite-env.ts";
7
8export const MYSQL_API_PORT = 3000;
9
10export { Site };
11
12export const Db = Railway.MySQL("Db", {
13 project: Site,
14 environment: Partition,
15});
16
17/**
18 * HTTP Service that binds MySQL via {@link Railway.ConnectMySQL}
19 * and answers SELECT 1. Never returns the connection string.
20 */
21export default class MySQLApi extends Railway.Service<MySQLApi>()(
22 "MySQLApi",
23 {
24 project: Site,
25 environment: Partition,
26 main: import.meta.url,
27 port: MYSQL_API_PORT,
28 // Match Alchemy's installed version; multiple lockfile entries make an
29 // unversioned external ambiguous when bundling this fixture.
30 build: { install: { mysql2: "3.24.2" } },
31 },
32 Effect.gen(function* () {
33 const conn = yield* Railway.ConnectMySQL(Db);
34 const db = yield* Drizzle.MySQL(conn.connectionString);
35
36 return {
37 fetch: Effect.gen(function* () {
38 const request = yield* HttpServerRequest;
39 const path = new URL(request.url, "http://service").pathname;
40 if (path === "/ping") {
41 return yield* HttpServerResponse.json({ ok: true });
42 }
43 const rows = yield* db.execute("select 1 as ok", "objects");
44 if (path === "/health" || path === "/") {
45 return yield* HttpServerResponse.json({ rows });
46 }
47 return yield* HttpServerResponse.json({ rows }, { status: 404 });
48 }).pipe(
49 Effect.catch((error) =>
50 HttpServerResponse.json(
51 { ok: false, error: String(error) },
52 { status: 500 },
53 ),
54 ),
55 ),
56 };
57 }).pipe(Effect.provide(Railway.ConnectMySQLHttp)),
58) {}