141 lines
4.1 KiB
TypeScript
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;
|
|
}
|