feat(server): add project sharing access control

This commit is contained in:
2026-07-10 14:46:18 +08:00
parent 60130b3d9b
commit 9d47cb9bbd
41 changed files with 3069 additions and 17 deletions
@@ -72,6 +72,46 @@ ALTER TABLE projects ADD COLUMN IF NOT EXISTS snapshot_id TEXT NOT NULL DEFAULT
ALTER TABLE projects ADD COLUMN IF NOT EXISTS last_thread_id TEXT NOT NULL DEFAULT '';
CREATE INDEX IF NOT EXISTS projects_user_updated_idx ON projects(user_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS project_share_policies (
project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash BYTEA NOT NULL UNIQUE,
token_ciphertext BYTEA NOT NULL,
link_permission TEXT NOT NULL DEFAULT 'private' CHECK (link_permission IN ('private', 'canvas', 'viewer', 'editor')),
policy_version BIGINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS project_share_policies_owner_idx ON project_share_policies(owner_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS project_share_members (
id UUID PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES project_share_policies(project_id) ON DELETE CASCADE,
identifier_type TEXT NOT NULL CHECK (identifier_type IN ('email', 'phone')),
identifier_hash BYTEA NOT NULL,
identifier_ciphertext BYTEA NOT NULL,
permission TEXT NOT NULL CHECK (permission IN ('canvas', 'viewer', 'editor')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(project_id, identifier_type, identifier_hash)
);
CREATE INDEX IF NOT EXISTS project_share_members_project_idx ON project_share_members(project_id, created_at ASC);
CREATE TABLE IF NOT EXISTS project_share_visits (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES project_share_policies(project_id) ON DELETE CASCADE,
visitor_hash BYTEA NOT NULL,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
visit_count BIGINT NOT NULL DEFAULT 1,
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(project_id, visitor_hash)
);
CREATE INDEX IF NOT EXISTS project_share_visits_project_seen_idx ON project_share_visits(project_id, last_seen_at DESC);
CREATE TABLE IF NOT EXISTS canvas_nodes (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,