import cors from "@fastify/cors"; import jwt from "@fastify/jwt"; import multipart from "@fastify/multipart"; import Fastify from "fastify"; import { ZodError } from "zod"; import { MAX_IMAGE_UPLOAD_BYTES, mediaStorage } from "./media-storage.js"; import { prisma } from "./prisma.js"; import { registerAdminRoutes } from "./routes/admin.js"; import { registerMemoryRoutes } from "./routes/memory.js"; import { registerPublicRoutes } from "./routes/public.js"; import { registerWecomRoutes } from "./routes/wecom.js"; export async function createServer() { const app = Fastify({ logger: { level: process.env.LOG_LEVEL ?? "info", }, }); await app.register(cors, { origin: true, credentials: true, }); app.addContentTypeParser(["application/xml", "text/xml"], { parseAs: "string" }, (_request, body, done) => { done(null, body); }); await app.register(jwt, { secret: process.env.JWT_SECRET ?? "dev-only-change-me-before-production", }); await mediaStorage.ensureReady(); await app.register(multipart, { limits: { files: 1, fileSize: MAX_IMAGE_UPLOAD_BYTES, }, }); app.setErrorHandler((error: unknown, _request, reply) => { if (error instanceof ZodError) { app.log.warn(error); reply.status(400).send({ message: error.issues[0]?.message ?? "请求参数不正确", }); return; } const normalized = error instanceof Error ? error : new Error("Unknown server error"); const maybeStatus = "statusCode" in normalized && typeof normalized.statusCode === "number" ? normalized.statusCode : undefined; const statusCode = maybeStatus && maybeStatus >= 400 ? maybeStatus : 500; if (statusCode >= 500) app.log.error(normalized); else app.log.warn(normalized); reply.status(statusCode).send({ message: statusCode === 500 ? "服务暂时不可用" : normalized.message, }); }); if (process.env.MINIAPP_MEMORY_DB === "1") { if (process.env.MINIAPP_ALLOW_EPHEMERAL_DATA !== "1") { throw new Error("检测到 MINIAPP_MEMORY_DB=1,但未显式允许临时数据。请使用 npm run dev:api:memory,或配置 DATABASE_URL 后使用 npm run dev:api。"); } app.log.warn("临时内存模式已启用:后台配置只保存在当前进程,API 重启后会恢复默认。需要持久化请配置 DATABASE_URL 并使用 npm run dev:api。"); await registerMemoryRoutes(app); } else { if (!process.env.DATABASE_URL) { throw new Error("DATABASE_URL 未配置。首页后台配置需要 PostgreSQL 持久化;请复制 .env.example 配置数据库,或仅在临时演示时使用 npm run dev:api:memory。"); } await prisma.$connect(); app.log.info("PostgreSQL 持久化模式已启用。"); app.addHook("onClose", async () => { await prisma.$disconnect(); }); await registerPublicRoutes(app); await registerAdminRoutes(app); await registerWecomRoutes(app); } return app; }