8d01553469
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>
53 lines
2.0 KiB
JavaScript
53 lines
2.0 KiB
JavaScript
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);
|
|
}
|