feat(page-data): complete Phase 4-5, ops UI, and publish integration

Add visitor roles, row-level scope, owner ops APIs, MySQL policy index,
Turnstile captcha, browser client SDK, publish-panel dataset binding,
acceptance tests, and usage documentation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-08 14:52:49 +08:00
parent 8d01553469
commit 6b0c633a75
40 changed files with 4056 additions and 60 deletions
+80
View File
@@ -601,6 +601,86 @@ export type MindSpacePublicationStats = {
sources: Array<{ source: string; views: number }>;
};
export type PageDataPolicyDataset = {
name?: string;
read: boolean;
insert: boolean;
update: boolean;
softDelete: boolean;
hardDelete?: boolean;
closed?: boolean;
closedAt?: string | null;
columns?: Record<string, string[]>;
rowPolicy?: { scope: 'all_rows' | 'own_rows' | 'owner_only'; ownerColumn?: string };
};
export type PageDataAccessPolicy = {
pageId: string;
ownerUserId: string;
accessMode: 'public' | 'password' | 'login_required';
defaultVisitorRole?: 'deny' | 'viewer' | 'editor';
visitors?: Record<string, 'viewer' | 'editor'>;
datasets: Record<string, PageDataPolicyDataset>;
updatedAt?: string;
};
export type PageDataLogEntry = {
at: string;
pageId?: string;
dataset?: string | null;
action: string;
rowId?: number | null;
accessMode?: string;
actor?: {
type?: string;
userId?: string | null;
visitorUserId?: string | null;
role?: string | null;
sessionId?: string | null;
};
summary?: Record<string, unknown> | null;
};
export type PageDataOpsOverview = {
pageId: string;
publication: {
id: string;
pageId: string;
accessMode: string;
status: string;
};
policy: PageDataAccessPolicy;
datasets: Array<{
name: string;
read: boolean;
insert: boolean;
update: boolean;
softDelete: boolean;
closed: boolean;
closedAt: string | null;
stats: { dataset: { name: string; table: string }; total: number } | null;
}>;
recentLogs: PageDataLogEntry[];
recentSubmissions: PageDataLogEntry[];
activeSessionCount: number;
index?: {
pageId: string;
accessMode: string;
datasetCount: number;
scopeHash: string;
updatedAt: number;
} | null;
registeredDatasets?: string[];
unconfiguredDatasets?: string[];
};
export type PageDataDatasetSummary = {
name: string;
table: string;
description: string;
actions: string[];
};
export type PlazaCategory = {
id: string;
name: string;