···11+/**
22+ * One-time data migration: copies permissions from roles.permissions[]
33+ * into the role_permissions join table.
44+ *
55+ * Safe to re-run (ON CONFLICT DO NOTHING).
66+ * Must be run AFTER migration 0011 (role_permissions table exists)
77+ * and BEFORE migration 0012 (permissions column is dropped).
88+ */
99+import postgres from "postgres";
1010+import { drizzle } from "drizzle-orm/postgres-js";
1111+import * as schema from "@atbb/db/schema";
1212+import { sql } from "drizzle-orm";
1313+1414+const databaseUrl = process.env.DATABASE_URL;
1515+if (!databaseUrl) {
1616+ console.error("DATABASE_URL is required");
1717+ process.exit(1);
1818+}
1919+2020+const client = postgres(databaseUrl);
2121+const db = drizzle(client, { schema });
2222+2323+async function run() {
2424+ // Read roles that still have permissions in the array column.
2525+ // We use raw SQL here because the Drizzle schema will have the
2626+ // permissions column removed by the time this script ships.
2727+ const roles = await db.execute(
2828+ sql`SELECT id, permissions FROM roles WHERE array_length(permissions, 1) > 0`
2929+ );
3030+3131+ if (roles.length === 0) {
3232+ console.log("No roles with permissions to migrate.");
3333+ await client.end();
3434+ return;
3535+ }
3636+3737+ let totalPermissions = 0;
3838+3939+ for (const role of roles) {
4040+ const roleId = role.id as bigint;
4141+ const permissions = role.permissions as string[];
4242+4343+ if (!permissions || permissions.length === 0) continue;
4444+4545+ // Insert each permission as a row, skip duplicates (idempotent)
4646+ await db.execute(
4747+ sql`INSERT INTO role_permissions (role_id, permission)
4848+ SELECT ${roleId}, unnest(${sql.raw(`ARRAY[${permissions.map((p: string) => `'${p.replace(/'/g, "''")}'`).join(",")}]`)}::text[])
4949+ ON CONFLICT DO NOTHING`
5050+ );
5151+5252+ totalPermissions += permissions.length;
5353+ console.log(` Role ${roleId}: migrated ${permissions.length} permissions`);
5454+ }
5555+5656+ console.log(
5757+ `\nMigrated ${totalPermissions} permissions across ${roles.length} roles.`
5858+ );
5959+ console.log("Safe to proceed with migration 0012 (drop permissions column).");
6060+6161+ await client.end();
6262+}
6363+6464+run().catch((err) => {
6565+ console.error("Migration failed:", err);
6666+ process.exit(1);
6767+});