f96fdcb3b9
Lock health recording behind confirm-only state machines, reject public publishes for the health category, and ship the 14-day experiment Page Data templates with tests. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.5 KiB
JavaScript
78 lines
2.5 KiB
JavaScript
import { HEALTH_CATEGORY_CODE, HEALTH_PUBLISH_POLICY } from './health-feature.mjs';
|
|
|
|
export const HEALTH_ALLOWED_ACCESS_MODES = Object.freeze(['password', 'login_required']);
|
|
|
|
export const HEALTH_SHARE_FORBIDDEN_COLUMNS = Object.freeze([
|
|
'raw_payload',
|
|
'source_ref',
|
|
'ocr_text',
|
|
]);
|
|
|
|
export const HEALTH_SHARE_FORBIDDEN_DATASETS = Object.freeze([
|
|
'health_observations',
|
|
'health_observation_drafts',
|
|
'health_documents',
|
|
]);
|
|
|
|
export function isHealthPublishPolicy(publishPolicy, categoryCode) {
|
|
return (
|
|
String(publishPolicy ?? '') === HEALTH_PUBLISH_POLICY
|
|
|| String(categoryCode ?? '') === HEALTH_CATEGORY_CODE
|
|
);
|
|
}
|
|
|
|
export function assertHealthPublicationAllowed({
|
|
categoryCode = null,
|
|
publishPolicy = null,
|
|
accessMode,
|
|
password = '',
|
|
existingPasswordHash = null,
|
|
} = {}) {
|
|
if (!isHealthPublishPolicy(publishPolicy, categoryCode)) {
|
|
return { ok: true, skipped: true };
|
|
}
|
|
const mode = String(accessMode ?? '').trim();
|
|
if (mode === 'public') {
|
|
const error = Object.assign(new Error('健康区禁止完全公开发布'), {
|
|
code: 'health_public_forbidden',
|
|
});
|
|
throw error;
|
|
}
|
|
if (!HEALTH_ALLOWED_ACCESS_MODES.includes(mode)) {
|
|
const error = Object.assign(new Error('健康区仅允许口令或登录访问'), {
|
|
code: 'health_access_mode_forbidden',
|
|
});
|
|
throw error;
|
|
}
|
|
if (mode === 'password') {
|
|
const hasPassword = String(password ?? '').trim().length > 0;
|
|
const hasHash = Boolean(existingPasswordHash);
|
|
if (!hasPassword && !hasHash) {
|
|
const error = Object.assign(new Error('健康区发布必须设置口令'), {
|
|
code: 'health_password_required',
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
return { ok: true, skipped: false, accessMode: mode };
|
|
}
|
|
|
|
export function assertHealthShareDatasetPolicy({ datasetName, columns = {} } = {}) {
|
|
const name = String(datasetName ?? '').trim();
|
|
if (HEALTH_SHARE_FORBIDDEN_DATASETS.includes(name)) {
|
|
const error = Object.assign(new Error('健康分享页不得直连原始健康 dataset'), {
|
|
code: 'health_share_raw_dataset_forbidden',
|
|
});
|
|
throw error;
|
|
}
|
|
const readColumns = Array.isArray(columns.read) ? columns.read : [];
|
|
const forbidden = readColumns.filter((column) => HEALTH_SHARE_FORBIDDEN_COLUMNS.includes(column));
|
|
if (forbidden.length > 0) {
|
|
const error = Object.assign(new Error(`健康分享页不得暴露字段:${forbidden.join(', ')}`), {
|
|
code: 'health_share_column_forbidden',
|
|
});
|
|
throw error;
|
|
}
|
|
return { ok: true };
|
|
}
|