import Fastify, { type FastifyError } from "fastify"; import { z } from "zod"; import { checkOllamaHealth, generateResponse } from "./ollama.js"; const echoSchema = z.object({ message: z.string().min(1).max(500), }); export const buildApp = ( generate = generateResponse, checkHealth = checkOllamaHealth, ) => { const app = Fastify({ logger: true, }); app.setErrorHandler((error: FastifyError, request, reply) => { request.log.error(error); const candidateStatusCode = error.statusCode ?? 500; const statusCode = candidateStatusCode >= 400 && candidateStatusCode < 500 ? candidateStatusCode : 500; return reply.code(statusCode).send({ error: statusCode === 500 ? "Internal Server Error" : "Request Error", }); }); app.setNotFoundHandler((_request, reply) => { return reply.code(404).send({ error: "Not Found", }); }); app.get("/health", async () => ({ status: "ok", })); app.get("/ready", async (_request, reply) => { const ollamaReady = await checkHealth(); if (!ollamaReady) { return reply.code(503).send({ status: "not-ready", dependencies: { ollama: "unavailable", }, }); } return { status: "ready", dependencies: { ollama: "available", }, }; }); app.post("/echo", async (request, reply) => { const result = echoSchema.safeParse(request.body); if (!result.success) { return reply.code(400).send({ error: "Invalid request body", }); } return { message: result.data.message, }; }); app.post("/generate", async (request, reply) => { const result = echoSchema.safeParse(request.body); if (!result.success) { return reply.code(400).send({ error: "Invalid request body", }); } try { const response = await generate(result.data.message); return { response }; } catch (error) { request.log.error({ err: error }, "Ollama generation failed"); return reply.code(503).send({ error: "Generation service unavailable", }); } }); app.get("/test-error", async () => { throw new Error("Sensitive internal detail"); }); return app; }; const start = async () => { const app = buildApp(); try { await app.listen({ host: "127.0.0.1", port: 3000, }); } catch (error) { app.log.error(error); process.exit(1); } }; if (import.meta.url === `file://${process.argv[1]}`) { void start(); }