feat(page-data): owner CRUD, public read, and role policy UI
Expose owner PATCH/soft-delete routes, allow anonymous read/stats on public pages when policy enables read, and add login_required role editor to the ops panel with tests and doc updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -174,6 +174,34 @@ test('acceptance: public insert only when authorized; read/update/delete denied
|
||||
assert.equal(deleteDenied.status, 403);
|
||||
});
|
||||
|
||||
test('acceptance: public read and stats when policy enables read', async () => {
|
||||
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'page-data-acc-public-read-'));
|
||||
const ownerService = await setupWorkspace(workspaceRoot);
|
||||
await ownerService.insertDatasetRow('leads', { name: '展示项', note: 'secret' });
|
||||
writePageAccessPolicy(workspaceRoot, {
|
||||
pageId: PAGE_ID,
|
||||
ownerUserId: OWNER_ID,
|
||||
accessMode: 'public',
|
||||
datasets: {
|
||||
leads: {
|
||||
read: true,
|
||||
columns: { read: ['id', 'name', 'created_at'] },
|
||||
},
|
||||
},
|
||||
});
|
||||
const app = buildApp(workspaceRoot);
|
||||
|
||||
const listed = await request(app, 'GET', `/api/public/pages/${PAGE_ID}/data/leads?limit=10`);
|
||||
assert.equal(listed.status, 200);
|
||||
assert.equal(listed.body.data.rows.length, 1);
|
||||
assert.equal(listed.body.data.rows[0].name, '展示项');
|
||||
assert.equal(listed.body.data.rows[0].note, undefined);
|
||||
|
||||
const stats = await request(app, 'GET', `/api/public/pages/${PAGE_ID}/data/leads/stats`);
|
||||
assert.equal(stats.status, 200);
|
||||
assert.equal(stats.body.data.total, 1);
|
||||
});
|
||||
|
||||
test('acceptance: unauthorized dataset/action/column and SQL-like keys are rejected', async () => {
|
||||
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'page-data-acc-deny-'));
|
||||
await setupWorkspace(workspaceRoot);
|
||||
|
||||
Reference in New Issue
Block a user