Show authenticated daily operation records

This commit is contained in:
wuyang
2026-11-03 20:57:42 +08:00
parent 84fc19c965
commit ad86a1661b
5 changed files with 95 additions and 5 deletions
+26
View File
@@ -67,6 +67,31 @@ export function createApp(options = process.env) {
return results.sort((a, b) => String(b.period_end || b.id).localeCompare(String(a.period_end || a.id)));
}
async function dailyRecords() {
let directory;
try { directory = await realpath(path.join(dataDir, 'daily')); }
catch (error) { if (error.code === 'ENOENT') return { records: [], invalid_count: 0 }; throw error; }
const actualRoot = await realpath(dataDir);
if (!directory.startsWith(actualRoot + path.sep)) throw new Error('Data path escapes DATA_DIR');
const names = await readdir(directory);
const fields = ['planned', 'executed', 'evidence', 'costs', 'review', 'tomorrow'];
const records = []; let invalidCount = 0;
for (const name of names.filter(n => /^\d{4}-\d{2}-\d{2}\.json$/.test(n)).sort().reverse()) {
const day = name.slice(0, -5);
const parsed = new Date(day + 'T00:00:00Z');
if (!Number.isFinite(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== day) { invalidCount++; continue; }
try {
const item = await jsonFile(`daily/${name}`, null);
if (!item || Array.isArray(item) || typeof item !== 'object' || item.date !== day ||
fields.some(field => item[field] !== undefined && (!Array.isArray(item[field]) || item[field].some(value => typeof value !== 'string')))) {
invalidCount++; continue;
}
records.push({ date: day, ...Object.fromEntries(fields.map(field => [field, item[field] || []])) });
} catch { invalidCount++; }
}
return { records, invalid_count: invalidCount };
}
function json(res, status, body, extra = {}) {
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', ...extra });
res.end(JSON.stringify(body));
@@ -130,6 +155,7 @@ export function createApp(options = process.env) {
if (route.startsWith('/api/')) {
if (!session(req)) return json(res, 401, { error: '请先登录' });
if (req.method === 'GET' && route === '/api/evolution') return json(res, 200, await jsonFile('evolution.json', { schema_version: 1, events: [] }));
if (req.method === 'GET' && route === '/api/daily') return json(res, 200, await dailyRecords());
if (req.method === 'GET' && route === '/api/dashboard') {
const list = await reports();
const config = await jsonFile('config.json', { title: 'PawPlay 运营台', timezone: 'Asia/Shanghai', budget: { billing_period: 'pending' } });