Files
memind/page-data-service.mjs

194 lines
6.0 KiB
JavaScript

import { createUserDataSpaceService } from './user-data-space-service.mjs';
import { listPageDataLogs } from './page-data-log-store.mjs';
import { readPageAccessPolicy } from './page-data-policy-store.mjs';
function mapServiceError(error) {
const code = error?.code;
if (code === 'dataset_not_found') {
return { status: 404, code: 'dataset_not_found', message: '数据集不存在' };
}
if (code === 'action_not_allowed') {
return { status: 403, code: 'action_not_allowed', message: error.message };
}
if (code === 'columns_not_allowed') {
return { status: 403, code: 'columns_not_allowed', message: error.message };
}
if (code === 'invalid_row_id' || code === 'restore_unsupported') {
return { status: 400, code: code ?? 'invalid_request', message: error.message };
}
if (code === 'invalid_payload' || code === 'payload_too_large' || code === 'invalid_dataset_config') {
return { status: 400, code: code ?? 'invalid_request', message: error.message };
}
if (code === 'table_not_found') {
return { status: 400, code: 'table_not_found', message: '数据集对应表不存在' };
}
return {
status: 400,
code: 'page_data_failed',
message: error instanceof Error ? error.message : '页面数据操作失败',
};
}
export function createPageDataService(deps = {}) {
const getUserAuth = deps.getUserAuth ?? (() => null);
const getPool = deps.getPool ?? (() => null);
const resolveWorkspaceRoot =
deps.resolveWorkspaceRoot ??
(async (user) => user?.workspaceRoot ?? null);
async function createServiceForUser(user) {
if (!user?.id) {
throw Object.assign(new Error('未授权'), { code: 'unauthorized' });
}
const workspaceRoot = await resolveWorkspaceRoot(user);
if (!workspaceRoot) {
throw Object.assign(new Error('用户工作区不存在'), { code: 'workspace_not_found' });
}
const pool = getPool();
return {
workspaceRoot,
service: createUserDataSpaceService({
workspaceRoot,
userId: user.id,
query: pool ?? null,
}),
};
}
async function listRows(user, datasetName, query = {}) {
const { service } = await createServiceForUser(user);
try {
return await service.readDatasetRows(datasetName, {
limit: query.limit,
offset: query.offset,
orderBy: query.orderBy ?? query.order_by,
orderDir: query.orderDir ?? query.order_dir,
includeDeleted: ['1', 'true', 'yes'].includes(String(query.include_deleted ?? '').toLowerCase()),
});
} catch (error) {
throw mapServiceError(error);
}
}
async function getSchema(user, datasetName) {
const { service } = await createServiceForUser(user);
try {
return await service.getDatasetSchema(datasetName);
} catch (error) {
throw mapServiceError(error);
}
}
async function getStats(user, datasetName) {
const { service } = await createServiceForUser(user);
try {
return await service.getDatasetStats(datasetName);
} catch (error) {
throw mapServiceError(error);
}
}
async function insertRow(user, datasetName, payload) {
const { service } = await createServiceForUser(user);
try {
return service.insertDatasetRow(datasetName, payload);
} catch (error) {
throw mapServiceError(error);
}
}
async function updateRow(user, datasetName, rowId, payload) {
const { service } = await createServiceForUser(user);
try {
return service.updateDatasetRow(datasetName, rowId, payload);
} catch (error) {
throw mapServiceError(error);
}
}
async function softDeleteRow(user, datasetName, rowId) {
const { service } = await createServiceForUser(user);
try {
return service.softDeleteDatasetRow(datasetName, rowId, { deletedBy: user.id });
} catch (error) {
throw mapServiceError(error);
}
}
async function restoreRow(user, datasetName, rowId) {
const { service } = await createServiceForUser(user);
try {
return service.restoreSoftDeletedRow(datasetName, rowId);
} catch (error) {
throw mapServiceError(error);
}
}
async function exportDataset(user, datasetName, query = {}) {
const { service } = await createServiceForUser(user);
try {
return service.exportDatasetRows(datasetName, {
format: query.format,
includeDeleted: ['1', 'true', 'yes'].includes(String(query.include_deleted ?? '').toLowerCase()),
limit: query.limit,
});
} catch (error) {
throw mapServiceError(error);
}
}
async function listDatasets(user) {
const { service } = await createServiceForUser(user);
const datasets = await service.listDatasets();
return datasets.map((dataset) => ({
name: dataset.name,
table: dataset.table,
description: dataset.description,
actions: dataset.actions,
}));
}
async function assertPageOwner(user, pageId) {
const pool = getPool();
if (pool) {
const [rows] = await pool.query(
`SELECT user_id FROM h5_publish_records
WHERE page_id = ? AND status = 'online'
ORDER BY published_at DESC
LIMIT 1`,
[pageId],
);
const ownerId = rows[0]?.user_id;
if (ownerId && ownerId === user.id) return;
}
const workspaceRoot = await resolveWorkspaceRoot(user);
const policy = readPageAccessPolicy(workspaceRoot, pageId);
if (policy?.ownerUserId === user.id) return;
throw Object.assign(new Error('无权访问该页面数据'), { code: 'forbidden', status: 403 });
}
async function listPageLogs(user, pageId, query = {}) {
await assertPageOwner(user, pageId);
const { workspaceRoot } = await createServiceForUser(user);
const logs = listPageDataLogs(workspaceRoot, pageId, {
limit: query.limit,
offset: query.offset,
});
return { pageId, logs, count: logs.length };
}
return {
getUserAuth,
listRows,
getSchema,
getStats,
insertRow,
updateRow,
softDeleteRow,
restoreRow,
exportDataset,
listDatasets,
listPageLogs,
};
}