From ed3e6df4d221b72e58e4829cbf4361ef2e6bdcf5 Mon Sep 17 00:00:00 2001 From: Max Koon <22125083+k2on@users.noreply.github.com> Date: Wed, 26 Nov 2025 02:38:00 -0500 Subject: [PATCH] feat: better auth api handler --- apps/api/src/index.ts | 87 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 203e90b..a7bd2e4 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -6,15 +6,22 @@ import * as HttpServerResponse from "@effect/platform/HttpServerResponse"; import * as HttpServerRequest from "@effect/platform/HttpServerRequest"; import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; +import * as NodeHttpServerRequest from "@effect/platform-node/NodeHttpServerRequest"; import { createServer } from "http"; import { + HttpApi, HttpApiBuilder, + HttpApiEndpoint, + HttpApiGroup, HttpApiSchema, HttpApiSecurity, + HttpMiddleware, } from "@effect/platform"; import { Schema, Data, Console } from "effect"; import { auth } from "./auth"; import { AuthSchema } from "@money/shared/auth"; +import { toNodeHandler } from "better-auth/node"; +import { BASE_URL } from "@money/shared"; class CurrentSession extends Context.Tag("CurrentSession")< CurrentSession, @@ -52,7 +59,7 @@ export interface AuthorizationImpl { class AuthorizationUnknownError extends Data.TaggedError( "AuthClientUnknownError", ) {} -class AuthorizationError extends Data.TaggedError("AuthClientFetchError")<{ +class AuthorizationError extends Data.TaggedError("AuthorizationError")<{ message: string; }> {} @@ -112,11 +119,87 @@ const HelloRoute = HttpLayerRouter.add( }), ).pipe(Layer.provide(SessionMiddleware.layer)); -const AllRoutes = Layer.mergeAll(HelloRoute); +const RootRoute = HttpLayerRouter.add( + "GET", + "/", + Effect.gen(function* () { + return HttpServerResponse.text("OK"); + }), +); + +const authHandler = Effect.gen(function* () { + const request = yield* HttpServerRequest.HttpServerRequest; + const nodeRequest = NodeHttpServerRequest.toIncomingMessage(request); + const nodeResponse = NodeHttpServerRequest.toServerResponse(request); + + nodeResponse.setHeader("Access-Control-Allow-Origin", "http://laptop:8081"); + nodeResponse.setHeader( + "Access-Control-Allow-Methods", + "GET, POST, PUT, DELETE, OPTIONS", + ); + nodeResponse.setHeader( + "Access-Control-Allow-Headers", + "Content-Type, Authorization, B3, traceparent, Cookie", + ); + nodeResponse.setHeader("Access-Control-Max-Age", "600"); + nodeResponse.setHeader("Access-Control-Allow-Credentials", "true"); + + // Handle preflight requests + if (nodeRequest.method === "OPTIONS") { + nodeResponse.statusCode = 200; + nodeResponse.end(); + + return; + // return nodeResponse; + } + + yield* Effect.tryPromise({ + try: () => toNodeHandler(auth)(nodeRequest, nodeResponse), + catch: (error) => { + return new AuthorizationError({ message: `${error}` }); + }, + }); + + // return nodeResponse; +}); + +export class AuthContractGroup extends HttpApiGroup.make("auth") + .add(HttpApiEndpoint.get("get", "/*")) + .add(HttpApiEndpoint.post("post", "/*")) + .add(HttpApiEndpoint.options("options", "/*")) + .prefix("/api/auth") {} + +export class DomainApi extends HttpApi.make("domain").add(AuthContractGroup) {} + +export const Api = HttpApi.make("api").addHttpApi(DomainApi); + +const AuthLive = HttpApiBuilder.group(Api, "auth", (handlers) => + handlers + .handle("get", () => authHandler.pipe(Effect.orDie)) + .handle("post", () => authHandler.pipe(Effect.orDie)) + .handle("options", () => authHandler.pipe(Effect.orDie)), +); + +const CorsMiddleware = HttpLayerRouter.middleware( + HttpMiddleware.cors({ + allowedOrigins: ["https://money.koon.us", `${BASE_URL}:8081`], + allowedMethods: ["POST", "GET", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, + }), + // { + // global: true, + // }, +); + +const AuthRoute = HttpLayerRouter.addHttpApi(Api).pipe(Layer.provide(AuthLive)); + +const AllRoutes = Layer.mergeAll(RootRoute, AuthRoute, HelloRoute); HttpLayerRouter.serve(AllRoutes).pipe( Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })), Layer.provide(AuthorizationLayer), + Layer.provide(CorsMiddleware.layer), Layer.launch, NodeRuntime.runMain, );