feat(page-data): add private and public dataset API (phase 1-3)
Extract UserDataSpaceService for shared SQLite access, wire logged-in Page Data routes, and add public insert plus password-token read/update/delete with policy storage, rate limits, and regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { normalizePageAccessPolicy } from './page-access-policy.mjs';
|
||||
|
||||
export function resolvePageDataPolicyDir(workspaceRoot) {
|
||||
return path.join(workspaceRoot, '.mindspace', 'page-data-policies');
|
||||
}
|
||||
|
||||
export function resolvePageDataPolicyPath(workspaceRoot, pageId) {
|
||||
const safePageId = String(pageId ?? '').trim();
|
||||
if (!safePageId || /[\\/]/.test(safePageId)) {
|
||||
throw Object.assign(new Error('pageId 无效'), { code: 'invalid_page_id' });
|
||||
}
|
||||
return path.join(resolvePageDataPolicyDir(workspaceRoot), `${safePageId}.json`);
|
||||
}
|
||||
|
||||
export function readPageAccessPolicy(workspaceRoot, pageId) {
|
||||
const policyPath = resolvePageDataPolicyPath(workspaceRoot, pageId);
|
||||
if (!fs.existsSync(policyPath)) return null;
|
||||
const raw = JSON.parse(fs.readFileSync(policyPath, 'utf8'));
|
||||
return normalizePageAccessPolicy(raw, { fallbackPageId: pageId });
|
||||
}
|
||||
|
||||
export function writePageAccessPolicy(workspaceRoot, policyInput) {
|
||||
const policy = normalizePageAccessPolicy(policyInput);
|
||||
const policyDir = resolvePageDataPolicyDir(workspaceRoot);
|
||||
fs.mkdirSync(policyDir, { recursive: true });
|
||||
const policyPath = resolvePageDataPolicyPath(workspaceRoot, policy.pageId);
|
||||
fs.writeFileSync(
|
||||
policyPath,
|
||||
`${JSON.stringify({ ...policy, updatedAt: new Date().toISOString() }, null, 2)}\n`,
|
||||
'utf8',
|
||||
);
|
||||
return readPageAccessPolicy(workspaceRoot, policy.pageId);
|
||||
}
|
||||
|
||||
export function deletePageAccessPolicy(workspaceRoot, pageId) {
|
||||
const policyPath = resolvePageDataPolicyPath(workspaceRoot, pageId);
|
||||
if (!fs.existsSync(policyPath)) return false;
|
||||
fs.unlinkSync(policyPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function listPageAccessPolicies(workspaceRoot) {
|
||||
const policyDir = resolvePageDataPolicyDir(workspaceRoot);
|
||||
if (!fs.existsSync(policyDir)) return [];
|
||||
return fs
|
||||
.readdirSync(policyDir)
|
||||
.filter((name) => name.endsWith('.json'))
|
||||
.map((name) => readPageAccessPolicy(workspaceRoot, name.replace(/\.json$/, '')))
|
||||
.filter(Boolean);
|
||||
}
|
||||
Reference in New Issue
Block a user