import { HttpApiSwagger } from "effect/unstable/httpapi"; import { HttpMiddleware, HttpRouter, HttpServerRequest, } from "effect/unstable/http"; import { Effect, Layer } from "effect"; import { ExecutorService, ExecutionEngineService } from "@executor-js/api/server"; import { OpenApiExtensionService } from "@executor-js/plugin-openapi/api"; import { McpExtensionService } from "@executor-js/plugin-mcp/api"; import { GraphqlExtensionService } from "@executor-js/plugin-graphql/api"; import { authorizeOrganization } from "../auth/authorize-organization"; import { WorkOSAuth } from "../auth/workos"; import { makeExecutionStack } from "../services/execution-stack"; import { HttpResponseError, isServerError, toErrorServerResponse } from "./error-response"; import { ProtectedCloudApi, ProtectedCloudApiLive, RouterConfig, SharedServices } from "./layers"; const lookupOrgForRequest = (request: HttpServerRequest.HttpServerRequest) => Effect.gen(function* () { const webRequest = yield* Effect.mapError( HttpServerRequest.toWeb(request), () => new HttpResponseError({ status: 500, code: "invalid_request", message: "Invalid request", }), ); const workos = yield* WorkOSAuth; const session = yield* workos.authenticateRequest(webRequest); if (!session || !session.organizationId) return null; const org = yield* authorizeOrganization(session.userId, session.organizationId); if (!org) return null; return { org, userId: session.userId }; }); const createProtectedApp = ( userId: string, organizationId: string, organizationName: string, ) => Effect.gen(function* () { const { executor, engine } = yield* makeExecutionStack( userId, organizationId, organizationName, ); const requestServices = Layer.mergeAll( Layer.succeed(ExecutorService)(executor), Layer.succeed(ExecutionEngineService)(engine), Layer.succeed(OpenApiExtensionService)(executor.openapi), Layer.succeed(McpExtensionService)(executor.mcp), Layer.succeed(GraphqlExtensionService)(executor.graphql), ); const protectedApiLayer = ProtectedCloudApiLive.pipe( Layer.provideMerge(HttpApiSwagger.layer(ProtectedCloudApi, { path: "/docs" })), Layer.provideMerge(RouterConfig), ); return yield* HttpRouter.toHttpEffect(protectedApiLayer).pipe( Effect.flatMap(HttpMiddleware.logger), Effect.provide(requestServices), ); }); export const ProtectedApiApp = Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest; const session = yield* lookupOrgForRequest(request); if (!session) { return yield* Effect.fail( new HttpResponseError({ status: 403, code: "no_organization", message: "No organization in session", }), ); } return yield* createProtectedApp(session.userId, session.org.id, session.org.name); }).pipe( Effect.provide(SharedServices), Effect.catchCause((err) => { if (isServerError(err)) { console.error("[api] request failed:", err instanceof Error ? err.stack : err); } return Effect.succeed(toErrorServerResponse(err)); }), );