From 042758d27c7d102fb698fa3ddecb82c48e1c5c01 Mon Sep 17 00:00:00 2001 From: Jorge Vargas Date: Sat, 10 May 2025 00:02:16 -0600 Subject: [PATCH] Migrate donator requests to limit of 5 --- .../202505091015_donator_request_limit.ts | 36 +++++++++++++++++++ prisma/migrate.ts | 35 ++++++++++++++++++ .../migration.sql | 7 ++++ .../migration.sql | 8 +++++ prisma/schema.prisma | 15 ++++++-- 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 prisma/data_migrations/202505091015_donator_request_limit.ts create mode 100644 prisma/migrate.ts create mode 100644 prisma/migrations/20250510041804_create_migration_table/migration.sql create mode 100644 prisma/migrations/20250510045915_request_state_as_enum/migration.sql diff --git a/prisma/data_migrations/202505091015_donator_request_limit.ts b/prisma/data_migrations/202505091015_donator_request_limit.ts new file mode 100644 index 0000000..15d1583 --- /dev/null +++ b/prisma/data_migrations/202505091015_donator_request_limit.ts @@ -0,0 +1,36 @@ +import { RequestState, type Prisma } from '@prisma/client' +import prismaClient from 'utils/prisma-client' + +const LIMIT_PENDING = 5 + +export default async function MigrationFn(tx: Prisma.TransactionClient) { + const donatorRequests = await tx.requests.findMany({ + where: { donator: true, state: RequestState.PENDING, userID: { not: null } } + }) + const donatorMap = new Map() + + donatorRequests.forEach((request) => { + const userID = request.userID as string + donatorMap.set(userID, [...(donatorMap.get(userID) || []), request]) + }) + + const holdRequests = new Set() + + for (const requests of donatorMap.values()) { + if (requests.length <= LIMIT_PENDING) continue + + const end = requests.length - LIMIT_PENDING + const sortedRequests = requests.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).slice(0, end) + + sortedRequests.forEach((r) => { + holdRequests.add(r.id) + }) + } + + await prismaClient.requests.updateMany({ + where: { id: { in: Array.from(holdRequests) } }, + data: { state: RequestState.HOLD } + }) + + console.log(`Updated ${holdRequests.size} requests to HOLD state`) +} diff --git a/prisma/migrate.ts b/prisma/migrate.ts new file mode 100644 index 0000000..f3d2001 --- /dev/null +++ b/prisma/migrate.ts @@ -0,0 +1,35 @@ +import { Prisma } from '@prisma/client' +import fs from 'fs' +import path from 'path' +import { createRequire } from 'module' +import prismaClient from 'utils/prisma-client' + +const require = createRequire(import.meta.url) + +export type MigrationFn = (tx: Prisma.TransactionClient) => Promise + +interface Migration { + id: string + migrationFn: MigrationFn +} + +const dataMigrationsPath = path.join(import.meta.dirname, 'data_migrations') +const migrationFiles = fs.readdirSync(dataMigrationsPath).filter((file) => file.endsWith('.ts')) +const migrations: Migration[] = migrationFiles + .sort() + .map((id) => ({ id, migrationFn: require(path.join(dataMigrationsPath, id)).default })) + .filter((migration) => migration.migrationFn !== undefined) + +for (const { id, migrationFn } of migrations) { + const startDate = new Date() + const migration = await prismaClient.migration.findFirst({ where: { id } }) + if (migration === null) { + console.log(`Migrating ${id}`) + await prismaClient.$transaction(async (tx) => { + await migrationFn(tx) + await tx.migration.create({ data: { id } }) + }) + const endDate = new Date() + console.log(`Migrated ${id} in ${(endDate.getTime() - startDate.getTime()) / 1000}s`) + } +} diff --git a/prisma/migrations/20250510041804_create_migration_table/migration.sql b/prisma/migrations/20250510041804_create_migration_table/migration.sql new file mode 100644 index 0000000..809e1fb --- /dev/null +++ b/prisma/migrations/20250510041804_create_migration_table/migration.sql @@ -0,0 +1,7 @@ +-- CreateTable +CREATE TABLE `Migration` ( + `id` VARCHAR(191) NOT NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/prisma/migrations/20250510045915_request_state_as_enum/migration.sql b/prisma/migrations/20250510045915_request_state_as_enum/migration.sql new file mode 100644 index 0000000..058f4f9 --- /dev/null +++ b/prisma/migrations/20250510045915_request_state_as_enum/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to alter the column `state` on the `requests` table. The data in that column could be lost. The data in that column will be cast from `VarChar(255)` to `Enum(EnumId(1))`. + +*/ +-- AlterTable +ALTER TABLE `requests` MODIFY `state` ENUM('complete', 'hold', 'pending') NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1f2a85a..ca23e1a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -334,13 +334,19 @@ model related_album { @@id([albumId, relatedId]) } +enum RequestState { + COMPLETE @map("complete") + HOLD @map("hold") + PENDING @map("pending") +} + model requests { id Int @id @default(autoincrement()) title String? @db.VarChar(255) link String? @db.VarChar(255) user String? @db.VarChar(255) userID String? @db.VarChar(255) - state String @db.VarChar(255) + state RequestState donator Boolean reason String? @db.VarChar(255) comments String? @db.VarChar(255) @@ -402,7 +408,7 @@ model submissions { model users { id String @id @db.VarChar(255) name String @db.VarChar(20) - username String? @unique @db.VarChar(255) + username String? @unique @db.VarChar(255) displayUsername String @default("Default display name") email String? @unique @db.VarChar(255) emailVerified Boolean @@ -461,3 +467,8 @@ model account { user users @relation(fields: [userId], references: [id]) } + +model Migration { + id String @id + createdAt DateTime @default(now()) +}