Files
WanderQ-MiniAPP2.0/apps/api/src/server.ts

81 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}