import { MigrationInterface, QueryRunner } from 'typeorm'; type LegacyServerRow = { id: string; name: string; description: string | null; ownerId: string; ownerPublicKey: string; passwordHash: string | null; isPrivate: number; maxUsers: number; currentUsers: number; createdAt: number; lastSeen: number; }; const SYSTEM_ROLE_IDS = { everyone: 'system-everyone', moderator: 'system-moderator', admin: 'system-admin' } as const; function buildDefaultServerRoles() { return [ { roleId: SYSTEM_ROLE_IDS.everyone, name: '@everyone', color: '#6b7280', position: 0, isSystem: 1, manageServer: 'inherit', manageRoles: 'inherit', manageChannels: 'inherit', manageIcon: 'inherit', kickMembers: 'inherit', banMembers: 'inherit', manageBans: 'inherit', deleteMessages: 'inherit', joinVoice: 'allow', shareScreen: 'allow', uploadFiles: 'allow' }, { roleId: SYSTEM_ROLE_IDS.moderator, name: 'Moderator', color: '#10b981', position: 200, isSystem: 1, manageServer: 'inherit', manageRoles: 'inherit', manageChannels: 'inherit', manageIcon: 'inherit', kickMembers: 'allow', banMembers: 'inherit', manageBans: 'inherit', deleteMessages: 'allow', joinVoice: 'inherit', shareScreen: 'inherit', uploadFiles: 'inherit' }, { roleId: SYSTEM_ROLE_IDS.admin, name: 'Admin', color: '#60a5fa', position: 300, isSystem: 1, manageServer: 'inherit', manageRoles: 'inherit', manageChannels: 'allow', manageIcon: 'allow', kickMembers: 'allow', banMembers: 'allow', manageBans: 'allow', deleteMessages: 'allow', joinVoice: 'inherit', shareScreen: 'inherit', uploadFiles: 'inherit' } ]; } export class ServerRoleAccessControl1000000000005 implements MigrationInterface { name = 'ServerRoleAccessControl1000000000005'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS "server_roles" ( "serverId" TEXT NOT NULL, "roleId" TEXT NOT NULL, "name" TEXT NOT NULL, "color" TEXT, "position" INTEGER NOT NULL, "isSystem" INTEGER NOT NULL DEFAULT 0, "manageServer" TEXT NOT NULL DEFAULT 'inherit', "manageRoles" TEXT NOT NULL DEFAULT 'inherit', "manageChannels" TEXT NOT NULL DEFAULT 'inherit', "manageIcon" TEXT NOT NULL DEFAULT 'inherit', "kickMembers" TEXT NOT NULL DEFAULT 'inherit', "banMembers" TEXT NOT NULL DEFAULT 'inherit', "manageBans" TEXT NOT NULL DEFAULT 'inherit', "deleteMessages" TEXT NOT NULL DEFAULT 'inherit', "joinVoice" TEXT NOT NULL DEFAULT 'inherit', "shareScreen" TEXT NOT NULL DEFAULT 'inherit', "uploadFiles" TEXT NOT NULL DEFAULT 'inherit', PRIMARY KEY ("serverId", "roleId") ) `); await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_server_roles_serverId" ON "server_roles" ("serverId")`); await queryRunner.query(` CREATE TABLE IF NOT EXISTS "server_user_roles" ( "serverId" TEXT NOT NULL, "userId" TEXT NOT NULL, "roleId" TEXT NOT NULL, "oderId" TEXT, PRIMARY KEY ("serverId", "userId", "roleId") ) `); await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_server_user_roles_serverId" ON "server_user_roles" ("serverId")`); await queryRunner.query(` CREATE TABLE IF NOT EXISTS "server_channel_permissions" ( "serverId" TEXT NOT NULL, "channelId" TEXT NOT NULL, "targetType" TEXT NOT NULL, "targetId" TEXT NOT NULL, "permission" TEXT NOT NULL, "value" TEXT NOT NULL, PRIMARY KEY ("serverId", "channelId", "targetType", "targetId", "permission") ) `); await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_server_channel_permissions_serverId" ON "server_channel_permissions" ("serverId")`); const servers = await queryRunner.query(` SELECT "id", "name", "description", "ownerId", "ownerPublicKey", "passwordHash", "isPrivate", "maxUsers", "currentUsers", "createdAt", "lastSeen" FROM "servers" `) as LegacyServerRow[]; for (const server of servers) { for (const role of buildDefaultServerRoles()) { await queryRunner.query( `INSERT OR REPLACE INTO "server_roles" ("serverId", "roleId", "name", "color", "position", "isSystem", "manageServer", "manageRoles", "manageChannels", "manageIcon", "kickMembers", "banMembers", "manageBans", "deleteMessages", "joinVoice", "shareScreen", "uploadFiles") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ server.id, role.roleId, role.name, role.color, role.position, role.isSystem, role.manageServer, role.manageRoles, role.manageChannels, role.manageIcon, role.kickMembers, role.banMembers, role.manageBans, role.deleteMessages, role.joinVoice, role.shareScreen, role.uploadFiles ] ); } } await queryRunner.query(` CREATE TABLE "servers_next" ( "id" TEXT PRIMARY KEY NOT NULL, "name" TEXT NOT NULL, "description" TEXT, "ownerId" TEXT NOT NULL, "ownerPublicKey" TEXT NOT NULL, "passwordHash" TEXT, "isPrivate" INTEGER NOT NULL DEFAULT 0, "maxUsers" INTEGER NOT NULL DEFAULT 0, "currentUsers" INTEGER NOT NULL DEFAULT 0, "slowModeInterval" INTEGER NOT NULL DEFAULT 0, "createdAt" INTEGER NOT NULL, "lastSeen" INTEGER NOT NULL ) `); await queryRunner.query(` INSERT INTO "servers_next" ("id", "name", "description", "ownerId", "ownerPublicKey", "passwordHash", "isPrivate", "maxUsers", "currentUsers", "slowModeInterval", "createdAt", "lastSeen") SELECT "id", "name", "description", "ownerId", "ownerPublicKey", "passwordHash", "isPrivate", "maxUsers", "currentUsers", 0, "createdAt", "lastSeen" FROM "servers" `); await queryRunner.query(`DROP TABLE "servers"`); await queryRunner.query(`ALTER TABLE "servers_next" RENAME TO "servers"`); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP TABLE IF EXISTS "server_channel_permissions"`); await queryRunner.query(`DROP TABLE IF EXISTS "server_user_roles"`); await queryRunner.query(`DROP TABLE IF EXISTS "server_roles"`); } }