NODEDC_TASKMANAGER_CODEXAPI/src/app.ts

141 lines
4.1 KiB
TypeScript

import Fastify, { type FastifyInstance } from "fastify";
import { ZodError } from "zod";
import type { AppConfig } from "./config.js";
import { createPool, DatabaseNotConfiguredError } from "./db/pool.js";
import { ToolExecutionInputError } from "./mcp/tool-runtime.js";
import { AgentsRepository } from "./repositories/agents.js";
import { registerAgentRoutes } from "./routes/agents.js";
import { registerHealthRoutes } from "./routes/health.js";
import { registerMcpRoutes } from "./routes/mcp.js";
import { registerPublicRoutes } from "./routes/public.js";
import { registerToolRoutes } from "./routes/tools.js";
import { ForbiddenError } from "./security/authorization.js";
import { UnauthorizedError } from "./security/bearer.js";
import { InternalAuthNotConfiguredError } from "./security/internal.js";
import { TaskerAdapterError, TaskerAdapterNotConfiguredError, TaskerAdapterUnavailableError, TaskerClient } from "./tasker/client.js";
export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
const pool = createPool(config);
const agentsRepository = pool ? new AgentsRepository(pool) : null;
const taskerClient = new TaskerClient({
baseUrl: config.NODEDC_TASKER_INTERNAL_URL,
internalAccessToken: config.NODEDC_INTERNAL_ACCESS_TOKEN,
});
const app = Fastify({
bodyLimit: 10 * 1024 * 1024,
logger: {
level: config.LOG_LEVEL,
},
});
app.addHook("onClose", async () => {
await pool?.end();
});
app.setErrorHandler((error, _request, reply) => {
if (error instanceof ZodError) {
void reply.status(400).send({
ok: false,
error: "validation_error",
details: error.issues,
});
return;
}
if (error instanceof DatabaseNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "database_not_configured",
message: "DATABASE_URL is required for Agent Gateway persistence endpoints.",
});
return;
}
if (error instanceof UnauthorizedError) {
void reply.status(401).send({
ok: false,
error: "unauthorized",
message: error.message,
});
return;
}
if (error instanceof InternalAuthNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "internal_auth_not_configured",
message: error.message,
});
return;
}
if (error instanceof ForbiddenError) {
void reply.status(403).send({
ok: false,
error: "forbidden",
message: error.message,
});
return;
}
if (error instanceof ToolExecutionInputError) {
void reply.status(error.httpStatus).send({
ok: false,
error: error.code,
message: error.message,
details: error.details,
});
return;
}
if (error instanceof TaskerAdapterNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "tasker_adapter_not_configured",
message: error.message,
});
return;
}
if (error instanceof TaskerAdapterError) {
void reply.status(error.statusCode).send({
ok: false,
error: "tasker_adapter_error",
message: error.message,
tasker_status: error.statusCode,
tasker_payload: error.payload,
});
return;
}
if (error instanceof TaskerAdapterUnavailableError) {
void reply.status(502).send({
ok: false,
error: "tasker_adapter_unavailable",
message: error.message,
});
return;
}
app.log.error(error);
void reply.status(500).send({
ok: false,
error: "internal_server_error",
message: "Agent Gateway request failed.",
});
});
await registerPublicRoutes(app);
await registerHealthRoutes(app, config, pool);
await registerAgentRoutes(app, {
agentsRepository,
publicUrl: config.NODEDC_AGENT_GATEWAY_PUBLIC_URL,
internalAccessToken: config.NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN,
});
await registerToolRoutes(app, { agentsRepository, taskerClient });
await registerMcpRoutes(app, { agentsRepository, taskerClient });
return app;
}