- M1-M2: 数据层与核心API(DuckDB, entries/tags/comments/search服务) - M3: SSR页面(EJS模板,接真实数据) - M4: 管理后台(登录/用户/内容源/AI ingest) - M5: 采集层(scheduler+rss/html/eryajf适配器) - M6: MCP只读接口(Streamable HTTP, 4工具) - M7: Docker部署(Dockerfile+docker-compose) - 测试: 10个用例全部通过 - 修复: DuckDB连接参数绑定、状态机迁移、标签树操作
66 lines
2.0 KiB
JavaScript
66 lines
2.0 KiB
JavaScript
import express from "express";
|
|
import session from "express-session";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { loadUser } from "./middleware/auth.js";
|
|
import { notFoundHandler, errorHandler } from "./middleware/errors.js";
|
|
import { pagesRouter } from "./routes/pages.routes.js";
|
|
import { entriesRouter } from "./routes/entries.routes.js";
|
|
import { tagsRouter } from "./routes/tags.routes.js";
|
|
import { commentsRouter } from "./routes/comments.routes.js";
|
|
import { adminRouter } from "./routes/admin.routes.js";
|
|
import { mcpRouter } from "./mcp/server.js";
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
export function buildApp(config) {
|
|
const app = express();
|
|
app.disable("x-powered-by");
|
|
app.set("view engine", "ejs");
|
|
app.set("views", path.join(here, "views"));
|
|
|
|
app.locals.config = config;
|
|
app.locals.fmtStars = (n) => {
|
|
const v = Number(n) || 0;
|
|
return v >= 1000 ? (v / 1000).toFixed(1) + "k" : String(v);
|
|
};
|
|
app.locals.fmtDate = (d) =>
|
|
d ? new Date(d).toISOString().slice(0, 10) : "—";
|
|
app.locals.qs = (obj) => {
|
|
const p = new URLSearchParams();
|
|
for (const [k, v] of Object.entries(obj)) {
|
|
if (Array.isArray(v)) {
|
|
if (v.length) p.set(k, v.join(","));
|
|
} else if (v !== undefined && v !== null && String(v) !== "") {
|
|
p.set(k, v);
|
|
}
|
|
}
|
|
const s = p.toString();
|
|
return s;
|
|
};
|
|
|
|
app.use(express.json({ limit: "1mb" }));
|
|
app.use(express.urlencoded({ extended: true }));
|
|
app.use(
|
|
session({
|
|
secret: config.sessionSecret,
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: { httpOnly: true, sameSite: "lax", maxAge: 1000 * 60 * 60 * 24 * 14 },
|
|
})
|
|
);
|
|
app.use(loadUser);
|
|
app.use(express.static(path.join(here, "..", "public")));
|
|
|
|
app.use(pagesRouter);
|
|
app.use(entriesRouter);
|
|
app.use(tagsRouter);
|
|
app.use(commentsRouter);
|
|
app.use(adminRouter);
|
|
app.use(mcpRouter);
|
|
|
|
app.use(notFoundHandler);
|
|
app.use(errorHandler);
|
|
return app;
|
|
}
|