完成
This commit is contained in:
143
server/stats-server.mjs
Normal file
143
server/stats-server.mjs
Normal file
@@ -0,0 +1,143 @@
|
||||
import { createServer } from "node:http";
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const statsFilePath = path.resolve(__dirname, "../data/generation-stats.json");
|
||||
const port = Number(process.env.PORT || 3001);
|
||||
|
||||
const defaultStats = {
|
||||
total: 0,
|
||||
byTemplate: {},
|
||||
updatedAt: null,
|
||||
};
|
||||
|
||||
let writeQueue = Promise.resolve();
|
||||
|
||||
async function ensureStatsFile() {
|
||||
await fs.mkdir(path.dirname(statsFilePath), { recursive: true });
|
||||
|
||||
try {
|
||||
await fs.access(statsFilePath);
|
||||
} catch {
|
||||
await fs.writeFile(statsFilePath, JSON.stringify(defaultStats, null, 2), "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
async function readStats() {
|
||||
await ensureStatsFile();
|
||||
const raw = await fs.readFile(statsFilePath, "utf8");
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return {
|
||||
total: Number(parsed.total || 0),
|
||||
byTemplate: parsed.byTemplate && typeof parsed.byTemplate === "object" ? parsed.byTemplate : {},
|
||||
updatedAt: parsed.updatedAt || null,
|
||||
};
|
||||
} catch {
|
||||
return { ...defaultStats };
|
||||
}
|
||||
}
|
||||
|
||||
function writeStats(nextStats) {
|
||||
writeQueue = writeQueue.then(() =>
|
||||
fs.writeFile(statsFilePath, JSON.stringify(nextStats, null, 2), "utf8"),
|
||||
);
|
||||
return writeQueue;
|
||||
}
|
||||
|
||||
function sendJson(res, statusCode, payload) {
|
||||
res.writeHead(statusCode, {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
});
|
||||
res.end(JSON.stringify(payload));
|
||||
}
|
||||
|
||||
function readRequestBody(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let body = "";
|
||||
|
||||
req.on("data", (chunk) => {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
req.on("end", () => {
|
||||
if (!body) {
|
||||
resolve({});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resolve(JSON.parse(body));
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
req.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
const server = createServer(async (req, res) => {
|
||||
if (!req.url || !req.method) {
|
||||
sendJson(res, 400, { error: "Invalid request." });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "OPTIONS") {
|
||||
sendJson(res, 204, {});
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
||||
|
||||
try {
|
||||
if (req.method === "GET" && url.pathname === "/api/stats") {
|
||||
const stats = await readStats();
|
||||
sendJson(res, 200, stats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && url.pathname === "/api/generate-count") {
|
||||
const body = await readRequestBody(req);
|
||||
const templateId = typeof body.templateId === "string" && body.templateId.trim()
|
||||
? body.templateId.trim()
|
||||
: "unknown";
|
||||
const stats = await readStats();
|
||||
const nextStats = {
|
||||
total: stats.total + 1,
|
||||
byTemplate: {
|
||||
...stats.byTemplate,
|
||||
[templateId]: Number(stats.byTemplate[templateId] || 0) + 1,
|
||||
},
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await writeStats(nextStats);
|
||||
sendJson(res, 200, nextStats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/api/health") {
|
||||
sendJson(res, 200, { ok: true });
|
||||
return;
|
||||
}
|
||||
|
||||
sendJson(res, 404, { error: "Not found." });
|
||||
} catch (error) {
|
||||
sendJson(res, 500, {
|
||||
error: error instanceof Error ? error.message : "Internal server error.",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(port, async () => {
|
||||
await ensureStatsFile();
|
||||
console.log(`Stats server listening on http://localhost:${port}`);
|
||||
});
|
||||
Reference in New Issue
Block a user