Files
zpan/migrations/0001_org_and_admin_plugins.sql
T
Jasper Van a1510906fb feat: add Better Auth organization + admin plugins (#241)
Enable org-aware architecture: auto-create personal org on signup,
first user promoted to admin (race-safe atomic SQL), requireAdmin
middleware, org context resolution in auth middleware.

- Add organization, member, invitation tables to auth schema
- Add admin plugin fields (role, banned, banReason, banExpires) to user
- Add session fields (impersonatedBy, activeOrganizationId)
- Rename storage_quotas → org_quotas, matters.uid → matters.org_id
- Add findPersonalOrg service with SQL-level metadata filtering
- Widen Database type to include both app and auth schemas
- Include auth schema in Cloudflare platform adapter
- Update shared types for org-aware API (OrgQuota, Organization, Member)
- Add DB migration 0001_org_and_admin_plugins
- Add tests for admin role, org creation, requireAdmin, findPersonalOrg
- Fix pre-existing shared package typecheck (add typescript devDep)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-01 16:20:02 -04:00

71 lines
2.9 KiB
SQL

-- Admin plugin: add fields to user table
ALTER TABLE `user` ADD `role` text;
--> statement-breakpoint
ALTER TABLE `user` ADD `banned` integer DEFAULT false;
--> statement-breakpoint
ALTER TABLE `user` ADD `ban_reason` text;
--> statement-breakpoint
ALTER TABLE `user` ADD `ban_expires` integer;
--> statement-breakpoint
-- Admin plugin: add impersonation tracking to session
ALTER TABLE `session` ADD `impersonated_by` text;
--> statement-breakpoint
-- Organization plugin: add active org to session
ALTER TABLE `session` ADD `active_organization_id` text;
--> statement-breakpoint
-- Organization plugin: create organization table
CREATE TABLE `organization` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`slug` text NOT NULL,
`logo` text,
`metadata` text,
`created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,
`updated_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer))
);
--> statement-breakpoint
CREATE UNIQUE INDEX `organization_slug_unique` ON `organization` (`slug`);
--> statement-breakpoint
-- Organization plugin: create member table
CREATE TABLE `member` (
`id` text PRIMARY KEY NOT NULL,
`organization_id` text NOT NULL,
`user_id` text NOT NULL,
`role` text DEFAULT 'member' NOT NULL,
`created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,
FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `member_organizationId_idx` ON `member` (`organization_id`);
--> statement-breakpoint
CREATE INDEX `member_userId_idx` ON `member` (`user_id`);
--> statement-breakpoint
-- Organization plugin: create invitation table
CREATE TABLE `invitation` (
`id` text PRIMARY KEY NOT NULL,
`organization_id` text NOT NULL,
`email` text NOT NULL,
`role` text NOT NULL,
`status` text DEFAULT 'pending' NOT NULL,
`expires_at` integer,
`created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,
`inviter_id` text NOT NULL,
FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`inviter_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `invitation_organizationId_idx` ON `invitation` (`organization_id`);
--> statement-breakpoint
CREATE INDEX `invitation_email_idx` ON `invitation` (`email`);
--> statement-breakpoint
-- Schema change: rename matters.uid to matters.org_id
ALTER TABLE `matters` RENAME COLUMN `uid` TO `org_id`;
--> statement-breakpoint
-- Schema change: rename storage_quotas to org_quotas with new structure
ALTER TABLE `storage_quotas` RENAME TO `org_quotas`;
--> statement-breakpoint
ALTER TABLE `org_quotas` RENAME COLUMN `uid` TO `org_id`;
--> statement-breakpoint
ALTER TABLE `org_quotas` DROP COLUMN `storage_id`;