import { AuthSchema } from "@money/shared/auth"; import { Context, Effect, Schema, Console } from "effect"; import { Authorization } from "../auth/context"; import { HttpLayerRouter, HttpServerRequest } from "@effect/platform"; export class CurrentSession extends Context.Tag("CurrentSession")< CurrentSession, { readonly auth: Schema.Schema.Type | null } >() {} export const SessionMiddleware = HttpLayerRouter.middleware<{ provides: CurrentSession; }>()( Effect.gen(function* () { const auth = yield* Authorization; return (httpEffect) => Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest; const headers = { ...request.headers }; const token = yield* HttpServerRequest.schemaHeaders( Schema.Struct({ authorization: Schema.optional(Schema.String), }), ).pipe( Effect.tap(Console.debug), Effect.flatMap(({ authorization }) => authorization != undefined ? parseAuthorization(authorization) : Effect.succeed(undefined), ), ); if (token) { headers["cookie"] = token; } const session = yield* auth .use((auth) => auth.api.getSession({ headers })) .pipe( Effect.flatMap((s) => s == null ? Effect.succeed(null) : Schema.decode(AuthSchema)(s), ), // Effect.tap((s) => Console.debug("Auth result", s)), ); return yield* Effect.provideService(httpEffect, CurrentSession, { auth: session, }); }); }), ); const parseAuthorization = (input: string) => Effect.gen(function* () { const m = /^Bearer\s+(.+)$/.exec(input); if (!m) { return yield* Effect.fail(new Error("Invalid token")); } return m[1]; });