93 lines · 3.3 KB · TypeScript
RawDownload
import {  ClientOrchestrationCommand,  OrchestrationDispatchCommandError,  OrchestrationGetSnapshotError,  type OrchestrationReadModel,} from "@t3tools/contracts";import { Effect } from "effect";import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"; import { ServerAuth } from "../auth/Services/ServerAuth.ts";import { normalizeDispatchCommand } from "./Normalizer.ts";import { OrchestrationEngineService } from "./Services/OrchestrationEngine.ts";import { ProjectionSnapshotQuery } from "./Services/ProjectionSnapshotQuery.ts"; const respondToOrchestrationHttpError = (  error: OrchestrationDispatchCommandError | OrchestrationGetSnapshotError,) =>  Effect.gen(function* () {    if (error._tag === "OrchestrationGetSnapshotError") {      yield* Effect.logError("orchestration http route failed", {        message: error.message,        cause: error.cause,      });      return HttpServerResponse.jsonUnsafe({ error: error.message }, { status: 500 });    }     return HttpServerResponse.jsonUnsafe({ error: error.message }, { status: 400 });  }); const authenticateOwnerSession = Effect.gen(function* () {  const request = yield* HttpServerRequest.HttpServerRequest;  const serverAuth = yield* ServerAuth;  const session = yield* serverAuth.authenticateHttpRequest(request);  if (session.role !== "owner") {    return yield* new OrchestrationDispatchCommandError({      message: "Only owner sessions can manage projects.",    });  }  return session;}); export const orchestrationSnapshotRouteLayer = HttpRouter.add(  "GET",  "/api/orchestration/snapshot",  Effect.gen(function* () {    yield* authenticateOwnerSession;    const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;    const snapshot = yield* projectionSnapshotQuery.getSnapshot().pipe(      Effect.mapError(        (cause) =>          new OrchestrationGetSnapshotError({            message: "Failed to load orchestration snapshot.",            cause,          }),      ),    );    return HttpServerResponse.jsonUnsafe(snapshot satisfies OrchestrationReadModel, {      status: 200,    });  }).pipe(    Effect.catchTag("OrchestrationDispatchCommandError", respondToOrchestrationHttpError),    Effect.catchTag("OrchestrationGetSnapshotError", respondToOrchestrationHttpError),  ),); export const orchestrationDispatchRouteLayer = HttpRouter.add(  "POST",  "/api/orchestration/dispatch",  Effect.gen(function* () {    yield* authenticateOwnerSession;    const orchestrationEngine = yield* OrchestrationEngineService;    const command = yield* HttpServerRequest.schemaBodyJson(ClientOrchestrationCommand).pipe(      Effect.mapError(        (cause) =>          new OrchestrationDispatchCommandError({            message: "Invalid orchestration command payload.",            cause,          }),      ),    );    const normalizedCommand = yield* normalizeDispatchCommand(command);    const result = yield* orchestrationEngine.dispatch(normalizedCommand).pipe(      Effect.mapError(        (cause) =>          new OrchestrationDispatchCommandError({            message: "Failed to dispatch orchestration command.",            cause,          }),      ),    );    return HttpServerResponse.jsonUnsafe(result, { status: 200 });  }).pipe(Effect.catchTag("OrchestrationDispatchCommandError", respondToOrchestrationHttpError)),);