diff --git a/prisma/migrations/20250520000000_add_admin_tables/migration.sql b/prisma/migrations/20250520000000_add_admin_tables/migration.sql new file mode 100644 index 0000000..e29748a --- /dev/null +++ b/prisma/migrations/20250520000000_add_admin_tables/migration.sql @@ -0,0 +1,62 @@ +-- CreateTable +CREATE TABLE `AdminUser` ( + `id` VARCHAR(191) NOT NULL, + `email` VARCHAR(255) NOT NULL, + `passwordHash` VARCHAR(255) NOT NULL, + `displayName` VARCHAR(100) NOT NULL, + `role` VARCHAR(32) NOT NULL DEFAULT 'ADMIN', + `status` VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', + `twoFactorEnabled` BOOLEAN NOT NULL DEFAULT false, + `twoFactorSecret` VARCHAR(100) NULL, + `lastLoginAt` DATETIME(3) NULL, + `lastLoginIp` VARCHAR(45) NULL, + `failedLoginCount` INTEGER NOT NULL DEFAULT 0, + `lockedUntil` DATETIME(3) NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + `deletedAt` DATETIME(3) NULL, + + UNIQUE INDEX `AdminUser_email_key`(`email`), + INDEX `AdminUser_email_idx`(`email`), + INDEX `AdminUser_status_idx`(`status`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `AdminSession` ( + `id` VARCHAR(191) NOT NULL, + `adminUserId` VARCHAR(191) NOT NULL, + `refreshTokenHash` VARCHAR(255) NOT NULL, + `ip` VARCHAR(45) NULL, + `userAgent` VARCHAR(500) NULL, + `expiresAt` DATETIME(3) NOT NULL, + `revokedAt` DATETIME(3) NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + INDEX `AdminSession_adminUserId_idx`(`adminUserId`), + INDEX `AdminSession_refreshTokenHash_idx`(`refreshTokenHash`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `AdminAuditLog` ( + `id` VARCHAR(191) NOT NULL, + `adminUserId` VARCHAR(191) NOT NULL, + `action` VARCHAR(64) NOT NULL, + `resourceType` VARCHAR(64) NULL, + `resourceId` VARCHAR(255) NULL, + `beforeJson` JSON NULL, + `afterJson` JSON NULL, + `ip` VARCHAR(45) NULL, + `userAgent` VARCHAR(500) NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + INDEX `AdminAuditLog_adminUserId_idx`(`adminUserId`), + INDEX `AdminAuditLog_action_idx`(`action`), + INDEX `AdminAuditLog_createdAt_idx`(`createdAt`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `AdminSession` ADD CONSTRAINT `AdminSession_adminUserId_fkey` FOREIGN KEY (`adminUserId`) REFERENCES `AdminUser`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a049995..7c7f936 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -714,6 +714,63 @@ model BackupJob { createdAt DateTime @default(now()) } +model AdminUser { + id String @id @default(cuid()) + email String @unique @db.VarChar(255) + passwordHash String @db.VarChar(255) + displayName String @db.VarChar(100) + role String @default("ADMIN") @db.VarChar(32) + status String @default("ACTIVE") @db.VarChar(32) + twoFactorEnabled Boolean @default(false) + twoFactorSecret String? @db.VarChar(100) + lastLoginAt DateTime? + lastLoginIp String? @db.VarChar(45) + failedLoginCount Int @default(0) + lockedUntil DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + + sessions AdminSession[] + + @@index([email]) + @@index([status]) +} + +model AdminSession { + id String @id @default(cuid()) + adminUserId String + refreshTokenHash String @db.VarChar(255) + ip String? @db.VarChar(45) + userAgent String? @db.VarChar(500) + expiresAt DateTime + revokedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + adminUser AdminUser @relation(fields: [adminUserId], references: [id]) + + @@index([adminUserId]) + @@index([refreshTokenHash]) +} + +model AdminAuditLog { + id String @id @default(cuid()) + adminUserId String + action String @db.VarChar(64) + resourceType String? @db.VarChar(64) + resourceId String? @db.VarChar(255) + beforeJson Json? + afterJson Json? + ip String? @db.VarChar(45) + userAgent String? @db.VarChar(500) + createdAt DateTime @default(now()) + + @@index([adminUserId]) + @@index([action]) + @@index([createdAt]) +} + model MembershipPlan { id String @id @default(cuid()) code String @unique @db.VarChar(32)