Files
geo/server/migrations/20260420100000_create_workspaces.up.sql
T
root b16e9f0bd1 feat(desktop): implement workspace foundation + desktop-client skeleton
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend
JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant,
thread workspace_id through tenant monitoring quota.

Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/
preload/renderer, shared Vue component package (packages/ui-shared), and server
surface — desktop client registration + token rotation + heartbeat, SSE task
event stream, desktop accounts/tasks/content handlers, publish job endpoint,
and supporting repositories, services, sqlc queries, and migrations.

Hard cutover per plan: remove browser-extension monitoring callback endpoints,
stub legacy media API in admin-web, and delete monitoring_callback_handler.go.
2026-04-19 14:18:20 +08:00

44 lines
1.5 KiB
SQL

CREATE TABLE workspaces (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
name TEXT NOT NULL,
slug TEXT NOT NULL,
is_default BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, slug)
);
CREATE UNIQUE INDEX idx_workspaces_tenant_default
ON workspaces (tenant_id)
WHERE is_default = TRUE;
CREATE TABLE workspace_memberships (
id BIGSERIAL PRIMARY KEY,
workspace_id BIGINT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
role TEXT NOT NULL DEFAULT 'member',
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (workspace_id, user_id)
);
CREATE UNIQUE INDEX idx_memberships_user_primary
ON workspace_memberships (user_id)
WHERE is_primary = TRUE;
INSERT INTO workspaces (tenant_id, name, slug, is_default)
SELECT id, 'Default', 'default', TRUE
FROM tenants
ON CONFLICT (tenant_id, slug) DO NOTHING;
INSERT INTO workspace_memberships (workspace_id, user_id, tenant_id, role, is_primary)
SELECT w.id, tm.user_id, tm.tenant_id, 'member', TRUE
FROM tenant_memberships tm
JOIN workspaces w
ON w.tenant_id = tm.tenant_id
AND w.is_default = TRUE
WHERE tm.deleted_at IS NULL
ON CONFLICT (workspace_id, user_id) DO NOTHING;