feat(mindspace): enforce PostgreSQL user data delivery

This commit is contained in:
john
2026-07-13 15:29:29 +08:00
parent b33c943b69
commit a6620fb719
57 changed files with 2779 additions and 164 deletions
+45 -9
View File
@@ -40,6 +40,33 @@ export function buildSmokeInsertRow(policy, datasetName) {
return row;
}
export function assessPageDataAuthenticationContract({ policy, html } = {}) {
const accessMode = String(policy?.accessMode ?? '').trim();
const content = String(html ?? '');
const usesServerAuthentication = /\.\s*authenticate\s*\(/.test(content);
const passwordValue = String.raw`\b(?:pwd|pass(?:word)?|passwordInput|adminPassword)\w*(?:\.value)?`;
const passwordComparison = String.raw`(?:===?|!==?)`;
const passwordOperand = String.raw`(?:[A-Za-z_$][\w$]*|['"\x60][^'"\x60]*['"\x60])`;
const usesClientSidePasswordCheck = new RegExp(
`(?:${passwordValue}\\s*${passwordComparison}\\s*${passwordOperand}|${passwordOperand}\\s*${passwordComparison}\\s*${passwordValue})`,
'i',
).test(content);
const reasons = [];
if (accessMode === 'password' && !usesServerAuthentication) {
reasons.push('password_page_missing_server_authentication');
}
if (accessMode === 'public' && usesServerAuthentication) {
reasons.push('public_page_cannot_use_password_authentication');
}
if (accessMode === 'public' && usesClientSidePasswordCheck) {
reasons.push('public_page_uses_client_side_password_gate');
}
if (accessMode === 'password' && usesClientSidePasswordCheck) {
reasons.push('password_page_uses_client_side_password_gate');
}
return reasons;
}
export async function queryOnlinePublication(pool, pageId) {
if (!pool || !pageId) return null;
const [rows] = await pool.query(
@@ -76,20 +103,13 @@ function findWorkspacePolicyForHtml({ publishDir, relativePath, html }) {
return null;
}
export function assessWorkspacePageDataReadiness({ publishDir, relativePath, html }) {
function assessWorkspacePageDataPolicyReadiness({ publishDir, relativePath, html }) {
const usage = detectPageDataDatasetUsageFromHtml(html);
if (!usage.size) {
return { ready: true, reasons: [] };
}
const reasons = [];
const dataSpace = createUserDataSpaceService({ workspaceRoot: publishDir });
for (const datasetName of usage.keys()) {
if (!dataSpace.getDataset(datasetName)) {
reasons.push(`dataset_not_registered:${datasetName}`);
}
}
const policy = findWorkspacePolicyForHtml({ publishDir, relativePath, html });
if (!policy) {
reasons.push('missing_workspace_policy');
@@ -98,6 +118,21 @@ export function assessWorkspacePageDataReadiness({ publishDir, relativePath, htm
return { ready: reasons.length === 0, reasons, policy };
}
export async function assessWorkspacePageDataReadiness({ userId, publishDir, relativePath, html }) {
const usage = detectPageDataDatasetUsageFromHtml(html);
const policyAssessment = assessWorkspacePageDataPolicyReadiness({ publishDir, relativePath, html });
if (!usage.size) return policyAssessment;
const reasons = [...policyAssessment.reasons];
const dataSpace = createUserDataSpaceService({ workspaceRoot: publishDir, userId });
for (const datasetName of usage.keys()) {
if (!(await dataSpace.getDataset(datasetName))) {
reasons.push(`dataset_not_registered:${datasetName}`);
}
}
return { ...policyAssessment, ready: reasons.length === 0, reasons };
}
export async function assessPageDataHtmlBinding({
pool,
userId,
@@ -111,7 +146,7 @@ export async function assessPageDataHtmlBinding({
return { bound: true, reasons: [], pageId: null };
}
const workspace = assessWorkspacePageDataReadiness({ publishDir, relativePath, html });
const workspace = await assessWorkspacePageDataReadiness({ userId, publishDir, relativePath, html });
const reasons = [...workspace.reasons];
let pageId = null;
@@ -139,6 +174,7 @@ export async function assessPageDataHtmlBinding({
if (String(policy.accessMode ?? '').trim() !== expectedAccessMode) {
reasons.push('policy_access_mode_mismatch');
}
reasons.push(...assessPageDataAuthenticationContract({ policy, html }));
for (const [datasetName, perms] of usage) {
const policyDataset = policy.datasets?.[datasetName];
if (!policyDataset) continue;