93 lines · 3.2 KB · TypeScript
RawDownload
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));  }),);