Files
memind/page-data-log-store.mjs
T
john 6b0c633a75 feat(page-data): complete Phase 4-5, ops UI, and publish integration
Add visitor roles, row-level scope, owner ops APIs, MySQL policy index,
Turnstile captcha, browser client SDK, publish-panel dataset binding,
acceptance tests, and usage documentation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 14:52:49 +08:00

48 lines
1.8 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
const DEFAULT_MAX_LOG_BYTES = 2 * 1024 * 1024;
export function resolvePageDataLogDir(workspaceRoot) {
return path.join(workspaceRoot, '.mindspace', 'page-data-logs');
}
export function resolvePageDataLogPath(workspaceRoot, pageId) {
const safePageId = String(pageId ?? '').trim();
if (!safePageId || /[\\/]/.test(safePageId)) {
throw Object.assign(new Error('pageId 无效'), { code: 'invalid_page_id' });
}
return path.join(resolvePageDataLogDir(workspaceRoot), `${safePageId}.jsonl`);
}
export function appendPageDataLog(workspaceRoot, pageId, entry, { maxBytes = DEFAULT_MAX_LOG_BYTES } = {}) {
const logPath = resolvePageDataLogPath(workspaceRoot, pageId);
fs.mkdirSync(path.dirname(logPath), { recursive: true });
const line = `${JSON.stringify({ ...entry, at: entry.at ?? new Date().toISOString() })}\n`;
fs.appendFileSync(logPath, line, 'utf8');
if (maxBytes > 0 && fs.existsSync(logPath) && fs.statSync(logPath).size > maxBytes) {
const raw = fs.readFileSync(logPath, 'utf8').trim().split('\n');
const trimmed = raw.slice(-Math.max(50, Math.floor(raw.length / 2)));
fs.writeFileSync(logPath, `${trimmed.join('\n')}\n`, 'utf8');
}
return line;
}
export function listPageDataLogs(workspaceRoot, pageId, { limit = 100, offset = 0 } = {}) {
const logPath = resolvePageDataLogPath(workspaceRoot, pageId);
if (!fs.existsSync(logPath)) return [];
const lines = fs.readFileSync(logPath, 'utf8').trim().split('\n').filter(Boolean);
const parsed = lines
.map((line) => {
try {
return JSON.parse(line);
} catch {
return null;
}
})
.filter(Boolean);
const safeOffset = Math.max(0, Number(offset ?? 0));
const safeLimit = Math.min(Math.max(1, Number(limit ?? 100)), 500);
return parsed.slice(safeOffset, safeOffset + safeLimit);
}