1import * as AWS from "alchemy/AWS";
2import { SQSQueueEventSource } from "alchemy/Server/SQSQueueEventSource";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import * as Stream from "effect/Stream";
6import * as HttpServerRequest from "effect/http/HttpServerRequest";
7import * as HttpServerResponse from "effect/http/HttpServerResponse";
8import { Network, NetworkLive } from "./Network.ts";
9
10export default class Server extends AWS.EC2.Instance<Server>()(
11 "ServerInstance",
12 Effect.gen(function* () {
13 const network = yield* Network;
14
15 return {
16 main: import.meta.url,
17 imageId: AWS.EC2.amazonLinux(),
18 instanceType: "t3.small",
19 subnetId: network.publicSubnetIds[0],
20 securityGroupIds: [network.appSecurityGroupId],
21 associatePublicIpAddress: true,
22 port: 3000,
23 };
24 }),
25 Effect.gen(function* () {
26 const queue = yield* AWS.SQS.Queue("JobsQueue", {
27 receiveMessageWaitTime: "20 seconds",
28 visibilityTimeout: "60 seconds",
29 });
30
31 yield* AWS.SQS.consumeQueueMessages(queue, (stream) =>
32 stream.pipe(Stream.mapEffect(Effect.logInfo), Stream.runDrain),
33 );
34
35 const sendMessage = yield* AWS.SQS.SendMessage(queue);
36
37 return {
38 fetch: Effect.gen(function* () {
39 const request = yield* HttpServerRequest.HttpServerRequest;
40 const url = new URL(request.originalUrl);
41
42 if (request.method === "GET" && url.pathname === "/") {
43 return yield* HttpServerResponse.json({
44 ok: true,
45 routes: ["GET /", "GET /enqueue?message=hello"],
46 });
47 }
48
49 if (request.method === "GET" && url.pathname === "/enqueue") {
50 const message = url.searchParams.get("message") ?? "hello from EC2";
51 const body = JSON.stringify({
52 message,
53 enqueuedAt: new Date().toISOString(),
54 });
55
56 const result = yield* sendMessage({
57 MessageBody: body,
58 });
59
60 return yield* HttpServerResponse.json({
61 ok: true,
62 message,
63 messageId: result.MessageId,
64 });
65 }
66
67 return HttpServerResponse.text("Not found", { status: 404 });
68 }).pipe(
69 Effect.catch(() =>
70 Effect.succeed(
71 HttpServerResponse.text("Internal server error", { status: 500 }),
72 ),
73 ),
74 ),
75 };
76 }).pipe(
77 Effect.provide(
78 Layer.provideMerge(
79 Layer.mergeAll(NetworkLive, SQSQueueEventSource),
80 Layer.mergeAll(
81 AWS.SQS.DeleteMessageBatchHttp,
82 AWS.SQS.ReceiveMessageHttp,
83 AWS.SQS.SendMessageHttp,
84 ),
85 ),
86 ),
87 ),
88) {}