import { FileSystem, HttpMiddleware, HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse, Path } from "@effect/platform" import { RpcServer } from "@effect/rpc" import { CommonConfig } from "@website/common/config" import { WebRpc, WebRpcSerializationDevelopment, WebRpcSerializationProduction } from "@website/common/webrpc" import { Duration, Effect, flow, Layer } from "effect" import { WebRpcLive } from "./webrpc" const router = HttpRouter.empty const makeWebRpcRoute = Effect.all([ CommonConfig.webRpcHttpPath, RpcServer.toHttpApp(WebRpc), ]).pipe( Effect.map(([path, app]) => HttpRouter.mountApp(path, app)), Effect.provide(WebRpcLive), ) const makeProductionWebappRoute = Effect.fnUntraced(function*(route: HttpRouter.PathInput) { const path = yield* Path.Path const fs = yield* FileSystem.FileSystem const dist = path.join(yield* path.fromFileUrl(new URL(".", import.meta.resolve("@website/webapp"))), "dist") return HttpRouter.all(route, Effect.gen(function*() { const req = yield* HttpServerRequest.HttpServerRequest const source = path.join(dist, req.url) const exists = yield* fs.stat(source).pipe( Effect.andThen(stat => stat.type === "File"), Effect.catchAll(() => Effect.succeed(false)), ) return yield* HttpServerResponse.setHeader( yield* HttpServerResponse.file(exists ? source : path.join(dist, "index.html")), "Cache-Control", `public, max-age=${Duration.toSeconds("365 days")}, immutable` ) })) }) export const HttpAppDevelopment = Layer.unwrapScoped(Effect.gen(function*() { return router.pipe( yield* Effect.provide(makeWebRpcRoute, WebRpcSerializationDevelopment), HttpServer.serve(flow(HttpMiddleware.logger, HttpMiddleware.xForwardedHeaders)), HttpServer.withLogAddress, ) })) export const HttpAppProduction = Layer.unwrapScoped(Effect.gen(function*() { return router.pipe( yield* Effect.provide(makeWebRpcRoute, WebRpcSerializationProduction), yield* makeProductionWebappRoute("*"), HttpServer.serve(HttpMiddleware.xForwardedHeaders), HttpServer.withLogAddress, ) }))