Skip to content

Commit 93e9c98

Browse files
committed
regen migrations
1 parent 75adcd3 commit 93e9c98

3 files changed

Lines changed: 15298 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
-- Rebase permission groups from organization-scoped to workspace-scoped.
2+
-- Each existing org-scoped permission_group is cloned onto every workspace
3+
-- owned by that org; members are copied to each clone only if the user has
4+
-- workspace-level permissions on the target workspace.
5+
6+
-- 0. Backfill workspace -> organization links for grandfathered workspaces whose
7+
-- billed account user is the sole owner of exactly one organization. This is a
8+
-- best-effort reconciliation: migration 0192 defaulted every pre-existing
9+
-- workspace to `grandfathered_shared` with `organization_id = NULL`, but many
10+
-- of those workspaces belong to users who own a single org. Without this link,
11+
-- the permission-group clone step below would drop all access control data for
12+
-- those workspaces. We only attach when ownership is unambiguous (user owns
13+
-- exactly one org) to avoid silently binding a workspace to the wrong org.
14+
UPDATE "workspace" w
15+
SET "organization_id" = owner_orgs."organization_id",
16+
"workspace_mode" = 'organization'::"workspace_mode"
17+
FROM (
18+
SELECT m."user_id", MIN(m."organization_id") AS "organization_id"
19+
FROM "member" m
20+
WHERE m."role" = 'owner'
21+
GROUP BY m."user_id"
22+
HAVING COUNT(*) = 1
23+
) AS owner_orgs
24+
WHERE w."organization_id" IS NULL
25+
AND w."workspace_mode" = 'grandfathered_shared'
26+
AND w."billed_account_user_id" = owner_orgs."user_id";--> statement-breakpoint
27+
28+
-- 1. Add workspace_id as nullable so existing rows can coexist during the data migration.
29+
ALTER TABLE "permission_group" ADD COLUMN "workspace_id" text;--> statement-breakpoint
30+
ALTER TABLE "permission_group" ADD CONSTRAINT "permission_group_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
31+
32+
-- 2. Materialize a plan of (source permission group, target workspace, new clone id)
33+
-- so we can insert the clone rows AND the member rows with stable references.
34+
CREATE TEMP TABLE "__permission_group_clone_plan" (
35+
"source_id" text NOT NULL,
36+
"cloned_id" text NOT NULL,
37+
"workspace_id" text NOT NULL
38+
) ON COMMIT DROP;--> statement-breakpoint
39+
40+
INSERT INTO "__permission_group_clone_plan" ("source_id", "cloned_id", "workspace_id")
41+
SELECT pg."id", gen_random_uuid()::text, w."id"
42+
FROM "permission_group" pg
43+
JOIN "workspace" w ON w."organization_id" = pg."organization_id"
44+
WHERE pg."organization_id" IS NOT NULL;--> statement-breakpoint
45+
46+
-- 3. Create the workspace-scoped clone rows using the planned ids.
47+
INSERT INTO "permission_group" (
48+
"id",
49+
"workspace_id",
50+
"organization_id",
51+
"name",
52+
"description",
53+
"config",
54+
"created_by",
55+
"created_at",
56+
"updated_at",
57+
"auto_add_new_members"
58+
)
59+
SELECT
60+
plan."cloned_id",
61+
plan."workspace_id",
62+
NULL,
63+
pg."name",
64+
pg."description",
65+
(pg."config" - 'hideEnvironmentTab' - 'hideTemplates'),
66+
pg."created_by",
67+
now(),
68+
now(),
69+
pg."auto_add_new_members"
70+
FROM "__permission_group_clone_plan" plan
71+
JOIN "permission_group" pg ON pg."id" = plan."source_id";--> statement-breakpoint
72+
73+
-- 4. Copy member rows to each workspace clone, but only if the user has
74+
-- workspace-level permissions on that target workspace.
75+
INSERT INTO "permission_group_member" ("id", "permission_group_id", "user_id", "assigned_by", "assigned_at")
76+
SELECT
77+
gen_random_uuid()::text,
78+
plan."cloned_id",
79+
m."user_id",
80+
m."assigned_by",
81+
m."assigned_at"
82+
FROM "__permission_group_clone_plan" plan
83+
JOIN "permission_group_member" m ON m."permission_group_id" = plan."source_id"
84+
WHERE EXISTS (
85+
SELECT 1 FROM "permissions" p
86+
WHERE p."entity_type" = 'workspace'
87+
AND p."entity_id" = plan."workspace_id"
88+
AND p."user_id" = m."user_id"
89+
) OR EXISTS (
90+
SELECT 1 FROM "workspace" w
91+
WHERE w."id" = plan."workspace_id"
92+
AND w."owner_id" = m."user_id"
93+
);--> statement-breakpoint
94+
95+
-- 5. Delete legacy org-scoped rows now that clones exist.
96+
DELETE FROM "permission_group_member"
97+
WHERE "permission_group_id" IN (
98+
SELECT "id" FROM "permission_group" WHERE "organization_id" IS NOT NULL
99+
);--> statement-breakpoint
100+
101+
DELETE FROM "permission_group" WHERE "organization_id" IS NOT NULL;--> statement-breakpoint
102+
103+
-- 6. Enforce NOT NULL on workspace_id now that every surviving row has one.
104+
ALTER TABLE "permission_group" ALTER COLUMN "workspace_id" SET NOT NULL;--> statement-breakpoint
105+
106+
-- 7. Drop legacy structures and swap indexes.
107+
ALTER TABLE "permission_group" DROP CONSTRAINT "permission_group_organization_id_organization_id_fk";--> statement-breakpoint
108+
DROP INDEX "permission_group_org_name_unique";--> statement-breakpoint
109+
DROP INDEX "permission_group_org_auto_add_unique";--> statement-breakpoint
110+
DROP INDEX "permission_group_member_user_id_unique";--> statement-breakpoint
111+
ALTER TABLE "permission_group" DROP COLUMN "organization_id";--> statement-breakpoint
112+
CREATE UNIQUE INDEX "permission_group_workspace_name_unique" ON "permission_group" USING btree ("workspace_id","name");--> statement-breakpoint
113+
CREATE UNIQUE INDEX "permission_group_workspace_auto_add_unique" ON "permission_group" USING btree ("workspace_id") WHERE auto_add_new_members = true;--> statement-breakpoint
114+
CREATE UNIQUE INDEX "permission_group_member_group_user_unique" ON "permission_group_member" USING btree ("permission_group_id","user_id");--> statement-breakpoint
115+
116+
-- 8. Sweep any residual dead config keys from pre-existing workspace-scoped rows (if any).
117+
UPDATE "permission_group" SET "config" = ("config" - 'hideEnvironmentTab' - 'hideTemplates') WHERE "config" ? 'hideEnvironmentTab' OR "config" ? 'hideTemplates';

0 commit comments

Comments
 (0)