feat: add org admin project onboarding foundation

This commit is contained in:
2026-07-10 00:25:00 +08:00
parent 2b7aa3294f
commit 34e07e229f
16 changed files with 1072 additions and 38 deletions
@@ -0,0 +1,92 @@
-- ADR-0021: org admin project onboarding.
-- Folder is a transparent project explorer node; project remains the permission boundary.
CREATE TABLE "OrganizationProjectSettings" (
"organizationId" TEXT NOT NULL,
"membersCanCreateProjects" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "OrganizationProjectSettings_pkey" PRIMARY KEY ("organizationId")
);
ALTER TABLE "OrganizationProjectSettings"
ADD CONSTRAINT "OrganizationProjectSettings_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
INSERT INTO "OrganizationProjectSettings" ("organizationId", "membersCanCreateProjects", "createdAt", "updatedAt")
SELECT "id", true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
FROM "Organization"
ON CONFLICT ("organizationId") DO NOTHING;
CREATE TABLE "Folder" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"parentId" TEXT,
"name" TEXT NOT NULL,
"sortKey" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"archivedAt" TIMESTAMP(3),
CONSTRAINT "Folder_pkey" PRIMARY KEY ("id")
);
ALTER TABLE "Folder"
ADD CONSTRAINT "Folder_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Folder"
ADD CONSTRAINT "Folder_parentId_fkey"
FOREIGN KEY ("parentId") REFERENCES "Folder"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
CREATE INDEX "Folder_organizationId_parentId_archivedAt_idx"
ON "Folder"("organizationId", "parentId", "archivedAt");
CREATE INDEX "Folder_organizationId_parentId_sortKey_idx"
ON "Folder"("organizationId", "parentId", "sortKey");
CREATE UNIQUE INDEX "Folder_active_sibling_name_key"
ON "Folder"("organizationId", COALESCE("parentId", ''), lower("name"))
WHERE "archivedAt" IS NULL;
INSERT INTO "Folder" ("id", "organizationId", "parentId", "name", "sortKey", "createdAt", "updatedAt")
SELECT 'folder_inbox_' || md5("id"), "id", NULL, 'Inbox', '000000', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
FROM "Organization";
ALTER TABLE "Project" ADD COLUMN "folderId" TEXT;
UPDATE "Project" AS p
SET "folderId" = f."id"
FROM "Folder" AS f
WHERE f."organizationId" = p."organizationId"
AND f."parentId" IS NULL
AND f."name" = 'Inbox'
AND p."folderId" IS NULL;
ALTER TABLE "Project"
ADD CONSTRAINT "Project_folderId_fkey"
FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
CREATE INDEX "Project_folderId_archivedAt_idx"
ON "Project"("folderId", "archivedAt");
ALTER TABLE "ProjectGroupBinding" ADD COLUMN "archivedAt" TIMESTAMP(3);
DROP INDEX IF EXISTS "ProjectGroupBinding_projectId_key";
DROP INDEX IF EXISTS "ProjectGroupBinding_chatId_key";
DROP INDEX IF EXISTS "ProjectGroupBinding_chatId_idx";
CREATE INDEX "ProjectGroupBinding_projectId_archivedAt_idx"
ON "ProjectGroupBinding"("projectId", "archivedAt");
CREATE INDEX "ProjectGroupBinding_chatId_archivedAt_idx"
ON "ProjectGroupBinding"("chatId", "archivedAt");
CREATE UNIQUE INDEX "ProjectGroupBinding_active_project_key"
ON "ProjectGroupBinding"("projectId")
WHERE "archivedAt" IS NULL;
CREATE UNIQUE INDEX "ProjectGroupBinding_active_chat_key"
ON "ProjectGroupBinding"("chatId")
WHERE "archivedAt" IS NULL;