1import * as Hetzner from "@/Hetzner";
2import * as Effect from "effect/Effect";
3
4export const VOLUME_PATH = "/var/lib/api";
5export const MARKER_FILE = `${VOLUME_PATH}/hello.txt`;
6export const MARKER = "hello-from-worker";
7export const API_PORT = 3000;
8
9export const Box = Hetzner.Server("Box", {
10 serverType: "cpx12",
11 image: "ubuntu-24.04",
12 location: "nbg1",
13});
14
15export const Data = Hetzner.Volume("Data", {
16 size: 10,
17 format: "ext4",
18 location: "nbg1",
19});
20
21export const Wall = Hetzner.Firewall(
22 "Wall",
23 Effect.gen(function* () {
24 const server = yield* Box;
25 return {
26 applyTo: [server],
27 rules: [
28 {
29 direction: "in" as const,
30 protocol: "tcp" as const,
31 port: "22",
32 sourceIps: ["0.0.0.0/0", "::/0"],
33 description: "ssh",
34 },
35 {
36 direction: "in" as const,
37 protocol: "tcp" as const,
38 port: String(API_PORT),
39 sourceIps: ["0.0.0.0/0", "::/0"],
40 description: "api",
41 },
42 ],
43 };
44 }),
45);
46
47export const Edge = Hetzner.LoadBalancer(
48 "Edge",
49 Effect.gen(function* () {
50 const server = yield* Box;
51 return {
52 location: "nbg1",
53 loadBalancerType: "lb11",
54 services: [
55 {
56 protocol: "http" as const,
57 listenPort: 80,
58 destinationPort: API_PORT,
59 healthCheck: {
60 protocol: "http" as const,
61 port: API_PORT,
62 interval: 3,
63 timeout: 2,
64 retries: 2,
65 http: { path: "/health" },
66 },
67 },
68 ],
69 targets: [{ type: "server" as const, server }],
70 };
71 }),
72);
73
74export const DnsZone = Hetzner.Zone("AppZone", {
75 ttl: 300,
76});
77
78export const AppRecord = Hetzner.RecordSet(
79 "App",
80 Effect.flatMap(Edge, (lb) =>
81 Effect.map(DnsZone, (zone) => ({
82 zone,
83 name: "app",
84 type: "A" as const,
85 records: [{ value: lb.ipv4.as<string>() }],
86 ttl: 300,
87 })),
88 ),
89);