From 984e661651fd64d6b720afa2273777d47d3f5df1 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:37:53 +0200 Subject: [PATCH 01/10] chore: sets up functions and limits for migrating users --- .../src/dobbelOmega/dobbelOmega.ts | 2 ++ .../src/dobbelOmega/migrateUsers.ts | 17 +++++++++++++++++ .../src/dobbelOmega/migrationLimits.ts | 2 ++ 3 files changed, 21 insertions(+) create mode 100644 src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts diff --git a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts index 7fd64db1d..d650a6a67 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts @@ -8,6 +8,7 @@ import migrateMailAliases from './migrateMailAlias' import migrateEvents from './migrateEvents' import { PrismaClient as PrismaClientVeven } from '@/generated/veven' import type { PrismaClient as PrismaClientPn } from '@/generated/pn' +import migrateUsers from './migrateUsers' /** * !DobbelOmega! @@ -23,6 +24,7 @@ export default async function dobbelOmega(pnPrisma: PrismaClientPn) { const imageCollectionIdMap = await migrateImageCollections(pnPrisma, vevenPrisma) const imageIdMap = await migrateImages(pnPrisma, vevenPrisma, imageCollectionIdMap, limits) + await migrateUsers(pnPrisma, vevenPrisma, limits) await migrateOmbul(pnPrisma, vevenPrisma, imageIdMap, limits) await migrateOmegaquotes(pnPrisma, vevenPrisma, limits) await migrateArticles(pnPrisma, vevenPrisma, imageIdMap, limits) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts new file mode 100644 index 000000000..dfad92a20 --- /dev/null +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -0,0 +1,17 @@ +import type { PrismaClient as PrismaClientPn } from '@/generated/pn' +import type { PrismaClient as PrismaClientVeven } from '@/generated/veven' +import type { Limits } from './migrationLimits' + +/** + * This function migrates users from Veven to PN + * @param pnPrisma - PrismaClientPn + * @param vevenPrisma - PrismaClientVeven + * @param limits - Limits - used to limit the number of users to migrate + */ +export default async function migrateUsers( + pnPrisma: PrismaClientPn, + vevenPrisma: PrismaClientVeven, + limits: Limits, +) { + +} \ No newline at end of file diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts b/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts index ad97b751b..b75926299 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts @@ -10,6 +10,7 @@ export function getLimits() { articles: 300, mailaliases: 0, events: null, + users: 100, } const nullObj: { [key in keyof typeof limits]: null } = { ombul: null, @@ -18,6 +19,7 @@ export function getLimits() { articles: null, mailaliases: null, events: null, + users: null, } const limitsOn = process.env.MIGRATION_WITH_LIMITS !== 'false' From e6302b9db620fb57646e49bd975b82d601ba5c92 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:40:33 +0200 Subject: [PATCH 02/10] chore: makes sure all profile images are migrated into profileimage collection --- .../src/dobbelOmega/dobbelOmega.ts | 2 +- .../src/dobbelOmega/migrateImages.ts | 10 ++ .../src/dobbelOmega/migrateUsers.ts | 122 +++++++++++++++++- src/prisma/prismaservice/src/seedOrder.ts | 4 +- 4 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts index d650a6a67..2e2802501 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts @@ -24,7 +24,7 @@ export default async function dobbelOmega(pnPrisma: PrismaClientPn) { const imageCollectionIdMap = await migrateImageCollections(pnPrisma, vevenPrisma) const imageIdMap = await migrateImages(pnPrisma, vevenPrisma, imageCollectionIdMap, limits) - await migrateUsers(pnPrisma, vevenPrisma, limits) + await migrateUsers(pnPrisma, vevenPrisma, limits, imageIdMap) await migrateOmbul(pnPrisma, vevenPrisma, imageIdMap, limits) await migrateOmegaquotes(pnPrisma, vevenPrisma, limits) await migrateArticles(pnPrisma, vevenPrisma, imageIdMap, limits) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts index 24cff972f..3a6298dd5 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts @@ -55,6 +55,13 @@ export default async function migrateImages( }) if (!ombulCollection) throw new Error('No ombul collection found for seeding images') + const profileCollection = await pnPrisma.imageCollection.findUnique({ + where: { + special: 'PROFILEIMAGES', + }, + }) + if (!profileCollection) throw new Error('No profile collection found for seeding images') + const images = await vevenPrisma.images.findMany({ include: { Ombul: true, @@ -67,6 +74,8 @@ export default async function migrateImages( let collectionId = vevenIdToPnId(migrateImageCollectionIdMap, image.ImageGroupId) if (image.Ombul.length) { collectionId = ombulCollection.id + } else if (image.UserId) { + collectionId = profileCollection.id } else if (!collectionId) { collectionId = garbageCollection.id } @@ -81,6 +90,7 @@ export default async function migrateImages( if (image.Articles.length) return true if (image.Events.length) return true if (image.collectionId === ombulCollection.id) return true + if (image.collectionId = profileCollection.id) return true if (image.ImageGroupId && image.ImageGroupId < limits.numberOffFullImageCollections) return true return false }) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index dfad92a20..04d55b369 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -1,6 +1,8 @@ -import type { PrismaClient as PrismaClientPn } from '@/generated/pn' -import type { PrismaClient as PrismaClientVeven } from '@/generated/veven' +import type { PrismaClient as PrismaClientPn, SEX } from '@/generated/pn' +import type { PrismaClient as PrismaClientVeven, enum_Users_sex } from '@/generated/veven' import type { Limits } from './migrationLimits' +import upsertOrderBasedOnDate from './upsertOrderBasedOnDate' +import { type IdMapper, vevenIdToPnId } from './IdMapper' /** * This function migrates users from Veven to PN @@ -12,6 +14,122 @@ export default async function migrateUsers( pnPrisma: PrismaClientPn, vevenPrisma: PrismaClientVeven, limits: Limits, + imageIdMap: IdMapper ) { + const users = await vevenPrisma.users.findMany({ + take: limits.users ? limits.users : undefined, + }) + const soelleGroup = await pnPrisma.omegaMembershipGroup.findUniqueOrThrow({ + where: { + omegaMembershipLevel: 'SOELLE' //Avsky! + }, + include: { + group: true + } + }) + const memberGroup = await pnPrisma.omegaMembershipGroup.findUniqueOrThrow({ + where: { + omegaMembershipLevel: 'MEMBER' + }, + include: { + group: true + } + }) + const { order: currentOrder } = await pnPrisma.omegaOrder.findFirstOrThrow({ + orderBy: { + order: 'desc' + } + }) + const classes = await pnPrisma.class.findMany({ + include: { + group: true + } + }) + + const yearIdMap = classes.reduce((acc, cur) => { + acc[cur.year] = cur.id + return acc + }, {} as Record) + + Promise.all(users.map(async user => { + const sexMap = { + m: "MALE", + f: "FEMALE", + other: "OTHER", + } satisfies Record + const pnUser = await pnPrisma.user.create({ + data: { + id: user.id, + username: user.username, + email: user.email ?? "dobbel@omega..no", + firstname: user.firstname, + lastname: user.lastname, + bio: user.bio ?? "", + acceptedTerms: undefined, + sex: sexMap[user.sex], + allergies: undefined, + mobile: undefined, + emailVerified: undefined, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + imageId: vevenIdToPnId(imageIdMap, user.ImageId), + + } + }) + + // Connect to correct membership group + const membershipOrder = await pnPrisma.omegaOrder.upsert({ + where: { order: user.order }, + create: { order: user.order }, + update: { order: user.order }, + }) + + const soelleOrder = await upsertOrderBasedOnDate(pnPrisma, user.createdAt) + + await pnPrisma.membership.create({ + data: { + groupId: soelleGroup.id, + userId: pnUser.id, + active: user.soelle, + admin: false, + order: soelleOrder, + } + }) + + if (!user.soelle) { + await pnPrisma.membership.create({ + data: { + groupId: memberGroup.id, + userId: pnUser.id, + active: true, + admin: false, + order: membershipOrder.order, + } + }) + } + + // connect to correct class (year) + if (user.yearOfStudy in [1, 2, 3, 4, 5]) { + let year = user.yearOfStudy + let order = currentOrder + while (year > 0) { + await pnPrisma.membership.create({ + data: { + groupId: soelleGroup.id, + userId: pnUser.id, + active: false, + admin: false, + order: order, + } + }) + year-- + order-- + } + } else { + if (user.yearOfStudy !== 6) { + console.error(`${user.id} (vevvenId) had bad yearOfStudy`) + } + } + })) } \ No newline at end of file diff --git a/src/prisma/prismaservice/src/seedOrder.ts b/src/prisma/prismaservice/src/seedOrder.ts index 9629e70b0..d7f55e1d5 100644 --- a/src/prisma/prismaservice/src/seedOrder.ts +++ b/src/prisma/prismaservice/src/seedOrder.ts @@ -3,13 +3,13 @@ import type { PrismaClient } from '@/generated/pn' export default async function seedOrder(prisma: PrismaClient) { await prisma.omegaOrder.upsert({ where: { - order: 105 + order: 106 }, update: { }, create: { - order: 105, + order: 106, } }) } From d1d26a297500483764d3f5704f475a86c496dd3e Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:21:28 +0200 Subject: [PATCH 03/10] chore: some spagetti to infer historic grades of users --- .../src/dobbelOmega/migrateUsers.ts | 187 ++++++++++++++++-- 1 file changed, 176 insertions(+), 11 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index 04d55b369..b2073c1e6 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -18,6 +18,13 @@ export default async function migrateUsers( ) { const users = await vevenPrisma.users.findMany({ take: limits.users ? limits.users : undefined, + include: { + StudyProgrammes: { + select: { + years: true + } + } + } }) const soelleGroup = await pnPrisma.omegaMembershipGroup.findUniqueOrThrow({ where: { @@ -110,25 +117,183 @@ export default async function migrateUsers( } // connect to correct class (year) - if (user.yearOfStudy in [1, 2, 3, 4, 5]) { - let year = user.yearOfStudy - let order = currentOrder - while (year > 0) { + const yearsInProgramme = Math.min(user.StudyProgrammes?.years ?? 0, 5) + switch (yearsInProgramme) { + case 0: + console.error(`User ${user.id} has no years in programme or no programme`) + break + case 2: + //ASSUME 2 years masters. The user can be member of 4 5 and 6 (siving) + if (user.yearOfStudy === 6) { + const orderBecameSiving = user.order + 2 + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[6], + userId: pnUser.id, + active: true, + admin: false, + order: orderBecameSiving, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[5], + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - 1, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[4], + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - 2, + } + }) + } else if (user.yearOfStudy == 5) { + const orderBecame5 = user.order + 1 + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[5], + userId: pnUser.id, + active: true, + admin: false, + order: orderBecame5, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[4], + userId: pnUser.id, + active: false, + admin: false, + order: orderBecame5 - 1, + } + }) + } else if (user.yearOfStudy == 4) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[4], + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) + } else { + console.error(`User ${user.id} is in a 2 year programme but not in year 4, 5 or 6`) + } + break + case 3: + //ASSUME 3 years bachelors. The user can be member of 1 2 and 3, and cannot be siving. + if (!(user.yearOfStudy in [1, 2, 3])) { + console.error(`User ${user.id} is in a 3 year programme but not in year 1, 2 or 3`) + break + } + if (user.yearOfStudy === 1) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[1], + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) + } else if (user.yearOfStudy === 2) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[2], + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) await pnPrisma.membership.create({ data: { - groupId: soelleGroup.id, + groupId: yearIdMap[1], userId: pnUser.id, active: false, admin: false, - order: order, + order: user.order - 1, + } + }) + } else if (user.yearOfStudy === 3) { + // This is a nut - it is hard to say if the user still is in 3. grade. + // We will assume that the user is in 3. grade if the order is gte 103 + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[3], + userId: pnUser.id, + active: user.order >= 103, + admin: false, + order: user.order, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[2], + userId: pnUser.id, + active: false, + admin: false, + order: user.order - 1, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[1], + userId: pnUser.id, + active: false, + admin: false, + order: user.order - 2, } }) - year-- - order-- } - } else { - if (user.yearOfStudy !== 6) { - console.error(`${user.id} (vevvenId) had bad yearOfStudy`) + break + case 5: + // Assuming 5 year masters. The user can be member of 1 2 3 4 5 and 6 (siving) + if (user.yearOfStudy === 6) { + const orderBecameSiving = user.order + 5 // Assume the user used 5 years to get to sivin + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[6], + userId: pnUser.id, + active: true, + admin: false, + order: orderBecameSiving, + } + }) + for (let i = 5; i >= 1; i--) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[i], + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - i, + } + }) + } + } else { + if (!(user.yearOfStudy in [1, 2, 3, 4, 5])) { + console.error(`User ${user.id} is in a 5 year programme but not in year 1, 2, 3, 4 or 5`) + break + } + for (let year=1; year <= user.yearOfStudy; year++) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap[year], + userId: pnUser.id, + active: year === user.yearOfStudy, + admin: false, + order: user.order + year - 1, + } + }) + } } } })) From 81d05ae4f07a40f681cc4b9cbb2c9b59f9f76dcc Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:27:12 +0200 Subject: [PATCH 04/10] chore: return userIdMap --- .../src/dobbelOmega/migrateUsers.ts | 22 +++++++++++-------- src/prisma/schema/user.prisma | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index b2073c1e6..ad567deab 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -5,7 +5,12 @@ import upsertOrderBasedOnDate from './upsertOrderBasedOnDate' import { type IdMapper, vevenIdToPnId } from './IdMapper' /** - * This function migrates users from Veven to PN + * TODO: Need migrate reservations (mail reservations) ?, and flairs + * This function migrates users from Veven to PN. It only migrates to theUser model, not FeideAccounts + * or Credentials. These should be linked when people log in for the first time. + * If a user has the soelle field true on veven it will get a relation to the soelle group + * - else it is assumed to be a member and an inactive relation to the soelle group. + * i.e. no users are assumed to be external. * @param pnPrisma - PrismaClientPn * @param vevenPrisma - PrismaClientVeven * @param limits - Limits - used to limit the number of users to migrate @@ -15,7 +20,7 @@ export default async function migrateUsers( vevenPrisma: PrismaClientVeven, limits: Limits, imageIdMap: IdMapper -) { +): Promise { const users = await vevenPrisma.users.findMany({ take: limits.users ? limits.users : undefined, include: { @@ -42,11 +47,6 @@ export default async function migrateUsers( group: true } }) - const { order: currentOrder } = await pnPrisma.omegaOrder.findFirstOrThrow({ - orderBy: { - order: 'desc' - } - }) const classes = await pnPrisma.class.findMany({ include: { @@ -59,7 +59,8 @@ export default async function migrateUsers( return acc }, {} as Record) - Promise.all(users.map(async user => { + const userIdMap: IdMapper = [] + await Promise.all(users.map(async user => { const sexMap = { m: "MALE", f: "FEMALE", @@ -81,9 +82,11 @@ export default async function migrateUsers( createdAt: user.createdAt, updatedAt: user.updatedAt, imageId: vevenIdToPnId(imageIdMap, user.ImageId), - + archived: user.archived, } }) + + userIdMap.push({ vevenId: user.id, pnId: pnUser.id }) // Connect to correct membership group const membershipOrder = await pnPrisma.omegaOrder.upsert({ @@ -297,4 +300,5 @@ export default async function migrateUsers( } } })) + return userIdMap } \ No newline at end of file diff --git a/src/prisma/schema/user.prisma b/src/prisma/schema/user.prisma index e0309e8df..0d793d437 100644 --- a/src/prisma/schema/user.prisma +++ b/src/prisma/schema/user.prisma @@ -11,6 +11,7 @@ model User { firstname String @default("[Fjernet]") lastname String @default("[Fjernet]") bio String @default("") + archived Boolean @default(false) acceptedTerms DateTime? sex SEX? allergies String? From 3eabf01b540081334b0640007ced311f7bfa117f Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Wed, 9 Oct 2024 22:09:20 +0200 Subject: [PATCH 05/10] chore: adds a dobbelomega logger --- docker-compose.yml | 3 + src/prisma/prismaservice/package-lock.json | 329 +++++++++++++++++- src/prisma/prismaservice/package.json | 3 +- .../src/dobbelOmega/migrateUsers.ts | 2 +- .../src/dobbelOmega/migrationLimits.ts | 2 +- src/prisma/prismaservice/src/logger/index.ts | 14 + src/prisma/prismaservice/src/seedOrder.ts | 18 +- 7 files changed, 349 insertions(+), 22 deletions(-) create mode 100644 src/prisma/prismaservice/src/logger/index.ts diff --git a/docker-compose.yml b/docker-compose.yml index a2c87caca..682d0513c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -82,6 +82,7 @@ services: API_KEY_ENCRYPTION_KEY: ${API_KEY_ENCRYPTION_KEY} volumes: - store:/usr/src/app/prismaservice/store + - dobbelOmegaManifest:/usr/src/app/prismaservice/dobbelOmegaManifest depends_on: db: condition: service_healthy @@ -145,3 +146,5 @@ volumes: driver: local logs: driver: local + dobbelOmegaManifest: + driver: local diff --git a/src/prisma/prismaservice/package-lock.json b/src/prisma/prismaservice/package-lock.json index ca3b9d6a7..8ac2ae3ef 100644 --- a/src/prisma/prismaservice/package-lock.json +++ b/src/prisma/prismaservice/package-lock.json @@ -15,7 +15,8 @@ "remark-rehype": "^11.1.1", "sharp": "^0.33.5", "unified": "^11.0.5", - "uuid": "^11.0.3" + "uuid": "^11.0.3", + "winston": "^3.15.0" }, "devDependencies": { "@prisma/client": "^5.22.0", @@ -29,6 +30,26 @@ "typescript": "^5.6.3" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", @@ -574,6 +595,12 @@ "undici-types": "~6.19.8" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", @@ -596,6 +623,18 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -713,6 +752,12 @@ "node": ">=0.10.0" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -770,6 +815,26 @@ "node": ">=0.10.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -815,6 +880,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1012,6 +1101,41 @@ "color-support": "bin.js" } }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/colorspace/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/colorspace/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/colorspace/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -1162,6 +1286,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1173,6 +1303,24 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -1347,6 +1495,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1359,6 +1513,12 @@ "node": ">=8" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1856,6 +2016,26 @@ "node": ">= 6" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -1878,8 +2058,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-accessor-descriptor": { "version": "1.0.1", @@ -2015,6 +2194,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -2048,6 +2239,29 @@ "node": ">=0.10.0" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2858,6 +3072,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -2963,6 +3186,15 @@ "fsevents": "2.3.3" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/property-information": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", @@ -3005,7 +3237,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3185,7 +3416,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -3210,6 +3440,15 @@ "ret": "~0.1.10" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -3505,6 +3744,15 @@ "node": ">=0.10.0" } }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -3547,7 +3795,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -3608,6 +3855,12 @@ "node": ">=10" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -3674,6 +3927,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/trough": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", @@ -4176,8 +4438,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "11.0.3", @@ -4265,6 +4526,58 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/winston": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.15.0.tgz", + "integrity": "sha512-RhruH2Cj0bV0WgNL+lOfoUBI4DVfdUNjVnJGVovWZmrcKtrFTTRzgXYK2O9cymSGjrERCtaAeHwMNnUWXlwZow==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.8.0.tgz", + "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==", + "license": "MIT", + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^4.5.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/src/prisma/prismaservice/package.json b/src/prisma/prismaservice/package.json index 5512f84b1..0284355b7 100644 --- a/src/prisma/prismaservice/package.json +++ b/src/prisma/prismaservice/package.json @@ -18,7 +18,8 @@ "remark-rehype": "^11.1.1", "sharp": "^0.33.5", "unified": "^11.0.5", - "uuid": "^11.0.3" + "uuid": "^11.0.3", + "winston": "^3.15.0" }, "prisma": { "schema": "../schema" diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index ad567deab..ebc1d6bf0 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -70,7 +70,7 @@ export default async function migrateUsers( data: { id: user.id, username: user.username, - email: user.email ?? "dobbel@omega..no", + email: user.email ?? `dobbel@omega.${user.id}.no`, firstname: user.firstname, lastname: user.lastname, bio: user.bio ?? "", diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts b/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts index b75926299..b3cc475f1 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrationLimits.ts @@ -9,7 +9,7 @@ export function getLimits() { omegaquotes: null, articles: 300, mailaliases: 0, - events: null, + events: 100, users: 100, } const nullObj: { [key in keyof typeof limits]: null } = { diff --git a/src/prisma/prismaservice/src/logger/index.ts b/src/prisma/prismaservice/src/logger/index.ts new file mode 100644 index 000000000..854676d7c --- /dev/null +++ b/src/prisma/prismaservice/src/logger/index.ts @@ -0,0 +1,14 @@ +import winston from 'winston' + +const logger = winston.createLogger({ + level: 'silly', +}) + +logger.add(new winston.transports.Console()) + +logger.add(new winston.transports.File({ + filename: 'manifest.log', + dirname: 'dobbelOmegaManifest', +})) + +export default logger \ No newline at end of file diff --git a/src/prisma/prismaservice/src/seedOrder.ts b/src/prisma/prismaservice/src/seedOrder.ts index d7f55e1d5..6cd93d599 100644 --- a/src/prisma/prismaservice/src/seedOrder.ts +++ b/src/prisma/prismaservice/src/seedOrder.ts @@ -1,15 +1,11 @@ import type { PrismaClient } from '@/generated/pn' export default async function seedOrder(prisma: PrismaClient) { - await prisma.omegaOrder.upsert({ - where: { - order: 106 - }, - update: { - - }, - create: { - order: 106, - } - }) + await Promise.all(Array.from({ length: 106 }, (_, i) => i + 1).map(async (order) => { + await prisma.omegaOrder.upsert({ + where: { order }, + update: {}, + create: { order }, + }) + })) } From 8bd25590e3e68ec039498a82ac428dd80c83a279 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:40:44 +0200 Subject: [PATCH 06/10] chore: links omegaQuotes to correct poster --- .../src/dobbelOmega/dobbelOmega.ts | 4 +- .../src/dobbelOmega/migrateOmegaquotes.ts | 47 ++++++++++++------ .../src/dobbelOmega/migrateUsers.ts | 49 ++++++++++++++++--- 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts index 2e2802501..1fbcca905 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts @@ -24,9 +24,9 @@ export default async function dobbelOmega(pnPrisma: PrismaClientPn) { const imageCollectionIdMap = await migrateImageCollections(pnPrisma, vevenPrisma) const imageIdMap = await migrateImages(pnPrisma, vevenPrisma, imageCollectionIdMap, limits) - await migrateUsers(pnPrisma, vevenPrisma, limits, imageIdMap) + const userIdMap = await migrateUsers(pnPrisma, vevenPrisma, limits, imageIdMap) await migrateOmbul(pnPrisma, vevenPrisma, imageIdMap, limits) - await migrateOmegaquotes(pnPrisma, vevenPrisma, limits) + await migrateOmegaquotes(pnPrisma, vevenPrisma, userIdMap, limits) await migrateArticles(pnPrisma, vevenPrisma, imageIdMap, limits) await migrateMailAliases(pnPrisma, vevenPrisma, limits) await migrateEvents(pnPrisma, vevenPrisma, imageIdMap, limits) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts index 3e57c20b6..c596a4626 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts @@ -1,6 +1,8 @@ import type { PrismaClient as PrismaClientPn } from '@/generated/pn' -import type { PrismaClient as PrismaClientVeven } from '@/generated/veven' +import type { PrismaClient as PrismaClientVeven, Quotes } from '@/generated/veven' import type { Limits } from './migrationLimits' +import { IdMapper, vevenIdToPnId } from './IdMapper' +import logger from '@/src/logger' /** * This function migrates omegaquotes from Veven to PN @@ -11,29 +13,42 @@ import type { Limits } from './migrationLimits' export default async function migrateOmegaquotes( pnPrisma: PrismaClientPn, vevenPrisma: PrismaClientVeven, + userIdMap: IdMapper, limits: Limits ) { const omegaquotes = await vevenPrisma.quotes.findMany({ take: limits.omegaquotes ? limits.omegaquotes : undefined, }) - //TODO: link to a user??? user migration not done yet and must be done early in the migration process - const user = await pnPrisma.user.create({ - data: { - email: '', - firstname: 'Omegaquotes migrator', - lastname: 'Migratorson', - username: 'omegaquotesMigrator', - }, - }) + // Remove all quotes without a PosterId. + // There exists no quotes without a PosterId in the database, except three tests + const omegaquoteFiltered = omegaquotes.reduce((acc, quote) => { + const PosterId = quote.PosterId + if (PosterId) { + acc.push({ ...quote, PosterId }) + } else { + logger.warn('Quote without PosterId found', quote) + } + return acc + }, [] as (Quotes & { PosterId: number })[]).reduce((acc, quote) => { + const pnPosterId = vevenIdToPnId(userIdMap, quote.PosterId) + if (!pnPosterId) { + logger.warn(`No user in PN found for quote - was the user with vevenId ${quote.PosterId} migrated`, quote) + return acc + } + acc.push({ ...quote, PosterId: pnPosterId }) + return acc + }, [] as (Quotes & { PosterId: number })[]) await pnPrisma.omegaQuote.createMany({ - data: omegaquotes.map(quote => ({ - quote: quote.quote, - author: quote.author, - timestamp: quote.timestamp || new Date(), - userPosterId: user.id, - })), + data: omegaquoteFiltered.map(quote => { + return { + quote: quote.quote, + author: quote.author, + timestamp: quote.timestamp || new Date(), + userPosterId: quote.PosterId, + } + }), }) //TODO: also seed bulshit into omegaquote diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index ebc1d6bf0..76a0ead1b 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -3,6 +3,41 @@ import type { PrismaClient as PrismaClientVeven, enum_Users_sex } from '@/genera import type { Limits } from './migrationLimits' import upsertOrderBasedOnDate from './upsertOrderBasedOnDate' import { type IdMapper, vevenIdToPnId } from './IdMapper' +import logger from '@/src/logger' + +function makeEmailUnique( + users: X[] +) : (X & { email: string })[] { + return users.map(user => { + let email = user.email ?? `dobbel@omega.${user.id}.no` + if (!user.email) { + logger.error(`User ${user.id} has no email`) + } else { + // Find users with same email if the email in null it is not a duplicate + const duplicates = users.filter(u => u.email === user.email) + if (duplicates.length > 1) { + logger.error(`User ${user.id} has duplicate email ${user.email}`) + email = `dobbel@omega.${user.id}.no` + } + } + return { ...user, email } + }) +} + +function makeUsernameUnique( + users: X[] +) : (X & { username: string })[] { + return users.map(user => { + let username = user.username + // Find users with same email if the email in null it is not a duplicate + const duplicates = users.filter(u => u.username === user.username) + if (duplicates.length > 1) { + logger.error(`User ${user.id} has duplicate username ${user.username}`) + username = username + user.id + } + return { ...user, username } + }) +} /** * TODO: Need migrate reservations (mail reservations) ?, and flairs @@ -21,7 +56,7 @@ export default async function migrateUsers( limits: Limits, imageIdMap: IdMapper ): Promise { - const users = await vevenPrisma.users.findMany({ + const users_ = await vevenPrisma.users.findMany({ take: limits.users ? limits.users : undefined, include: { StudyProgrammes: { @@ -31,6 +66,8 @@ export default async function migrateUsers( } } }) + const users = makeUsernameUnique(makeEmailUnique(users_)) + const soelleGroup = await pnPrisma.omegaMembershipGroup.findUniqueOrThrow({ where: { omegaMembershipLevel: 'SOELLE' //Avsky! @@ -70,7 +107,7 @@ export default async function migrateUsers( data: { id: user.id, username: user.username, - email: user.email ?? `dobbel@omega.${user.id}.no`, + email: user.email, firstname: user.firstname, lastname: user.lastname, bio: user.bio ?? "", @@ -123,7 +160,7 @@ export default async function migrateUsers( const yearsInProgramme = Math.min(user.StudyProgrammes?.years ?? 0, 5) switch (yearsInProgramme) { case 0: - console.error(`User ${user.id} has no years in programme or no programme`) + logger.error(`User ${user.id} has no years in programme or no programme`) break case 2: //ASSUME 2 years masters. The user can be member of 4 5 and 6 (siving) @@ -187,13 +224,13 @@ export default async function migrateUsers( } }) } else { - console.error(`User ${user.id} is in a 2 year programme but not in year 4, 5 or 6`) + logger.error(`User ${user.id} is in a 2 year programme but not in year 4, 5 or 6`) } break case 3: //ASSUME 3 years bachelors. The user can be member of 1 2 and 3, and cannot be siving. if (!(user.yearOfStudy in [1, 2, 3])) { - console.error(`User ${user.id} is in a 3 year programme but not in year 1, 2 or 3`) + logger.error(`User ${user.id} is in a 3 year programme but not in year 1, 2 or 3`) break } if (user.yearOfStudy === 1) { @@ -283,7 +320,7 @@ export default async function migrateUsers( } } else { if (!(user.yearOfStudy in [1, 2, 3, 4, 5])) { - console.error(`User ${user.id} is in a 5 year programme but not in year 1, 2, 3, 4 or 5`) + logger.error(`User ${user.id} is in a 5 year programme but not in year 1, 2, 3, 4 or 5`) break } for (let year=1; year <= user.yearOfStudy; year++) { From 409fd45d9afcca1c9a67bdce9152b469fb3f1d85 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:58:17 +0200 Subject: [PATCH 07/10] chore: takes some yearOfStudy nonsense from veven into account --- .../src/dobbelOmega/migrateUsers.ts | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index 76a0ead1b..8f7666e25 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -164,7 +164,16 @@ export default async function migrateUsers( break case 2: //ASSUME 2 years masters. The user can be member of 4 5 and 6 (siving) - if (user.yearOfStudy === 6) { + let yearOfStudy2 = user.yearOfStudy + if (yearOfStudy2 < 4) { + logger.error(`User ${user.id} is in 2 year programme but has year of study less than 4 - setting to 4`) + yearOfStudy2 = 4 + } + if (yearOfStudy2 > 6) { + logger.error(`User ${user.id} is in 2 year programme but has year of study greater than 6 - setting to 6`) + yearOfStudy2 = 6 + } + if (yearOfStudy2 === 6) { const orderBecameSiving = user.order + 2 await pnPrisma.membership.create({ data: { @@ -193,7 +202,7 @@ export default async function migrateUsers( order: orderBecameSiving - 2, } }) - } else if (user.yearOfStudy == 5) { + } else if (yearOfStudy2 == 5) { const orderBecame5 = user.order + 1 await pnPrisma.membership.create({ data: { @@ -213,7 +222,7 @@ export default async function migrateUsers( order: orderBecame5 - 1, } }) - } else if (user.yearOfStudy == 4) { + } else if (yearOfStudy2 == 4) { await pnPrisma.membership.create({ data: { groupId: yearIdMap[4], @@ -229,11 +238,16 @@ export default async function migrateUsers( break case 3: //ASSUME 3 years bachelors. The user can be member of 1 2 and 3, and cannot be siving. - if (!(user.yearOfStudy in [1, 2, 3])) { - logger.error(`User ${user.id} is in a 3 year programme but not in year 1, 2 or 3`) - break + let yearOfStudy3 = user.yearOfStudy + if (yearOfStudy3 < 1) { + logger.error(`User ${user.id} has year of study less than 1 - setting to 1`) + yearOfStudy3 = 1 + } + if (yearOfStudy3 > 3) { + logger.error(`User ${user.id} has year of study greater than 3 - setting to 3`) + yearOfStudy3 = 3 } - if (user.yearOfStudy === 1) { + if (yearOfStudy3 === 1) { await pnPrisma.membership.create({ data: { groupId: yearIdMap[1], @@ -243,7 +257,7 @@ export default async function migrateUsers( order: user.order, } }) - } else if (user.yearOfStudy === 2) { + } else if (yearOfStudy3 === 2) { await pnPrisma.membership.create({ data: { groupId: yearIdMap[2], @@ -262,7 +276,7 @@ export default async function migrateUsers( order: user.order - 1, } }) - } else if (user.yearOfStudy === 3) { + } else if (yearOfStudy3 === 3) { // This is a nut - it is hard to say if the user still is in 3. grade. // We will assume that the user is in 3. grade if the order is gte 103 await pnPrisma.membership.create({ @@ -296,7 +310,16 @@ export default async function migrateUsers( break case 5: // Assuming 5 year masters. The user can be member of 1 2 3 4 5 and 6 (siving) - if (user.yearOfStudy === 6) { + let yearOfStudy5 = user.yearOfStudy + if (yearOfStudy5 < 1) { + logger.error(`User ${user.id} is in 5 year programme but has year of study less than 1 - setting to 1`) + yearOfStudy5 = 1 + } + if (yearOfStudy5 > 6) { + logger.error(`User ${user.id} is in 5 year programme but has year of study greater than 6 - setting to 6`) + yearOfStudy5 = 6 + } + if (yearOfStudy5 === 6) { const orderBecameSiving = user.order + 5 // Assume the user used 5 years to get to sivin await pnPrisma.membership.create({ data: { @@ -319,11 +342,7 @@ export default async function migrateUsers( }) } } else { - if (!(user.yearOfStudy in [1, 2, 3, 4, 5])) { - logger.error(`User ${user.id} is in a 5 year programme but not in year 1, 2, 3, 4 or 5`) - break - } - for (let year=1; year <= user.yearOfStudy; year++) { + for (let year=1; year <= yearOfStudy5; year++) { await pnPrisma.membership.create({ data: { groupId: yearIdMap[year], From ec6c6874fe09caa81077d93eadb15e6c10f21f32 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:09:15 +0200 Subject: [PATCH 08/10] fix: user was seeded with veven id, change to auto increment --- docker-compose.dev.yml | 3 + .../src/development/seedDevUsers.ts | 2 +- .../src/dobbelOmega/dobbelOmega.ts | 9 +- .../src/dobbelOmega/migrateImages.ts | 2 +- .../src/dobbelOmega/migrateOmegaquotes.ts | 19 +- .../src/dobbelOmega/migrateUsers.ts | 477 +++++++++--------- src/prisma/prismaservice/src/logger/index.ts | 11 +- src/prisma/prismaservice/src/seedClasses.ts | 2 +- 8 files changed, 275 insertions(+), 250 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 1e06d949c..b0f362b82 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -87,6 +87,7 @@ services: volumes: - ${PROJECT_ROOT:-.}/src/prisma/schema:/usr/src/app/schema - devstore:/usr/src/app/prismaservice/store + - dobbelOmegaManifest:/usr/src/app/prismaservice/dobbelOmegaManifest depends_on: db: condition: service_healthy @@ -112,3 +113,5 @@ volumes: driver: local dotnext: driver: local + dobbelOmegaManifest: + driver: local diff --git a/src/prisma/prismaservice/src/development/seedDevUsers.ts b/src/prisma/prismaservice/src/development/seedDevUsers.ts index 5e9f074fd..649526c94 100644 --- a/src/prisma/prismaservice/src/development/seedDevUsers.ts +++ b/src/prisma/prismaservice/src/development/seedDevUsers.ts @@ -36,7 +36,7 @@ export default async function seedDevUsers(prisma: PrismaClient) { await Promise.all(ln.map(async (l, j) => { await prisma.user.upsert({ where: { - email: `${f}.${l}@${f}${l}.io` + username: `${f}${i}${j}` }, update: { diff --git a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts index 1fbcca905..3c2e52b53 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/dobbelOmega.ts @@ -6,9 +6,10 @@ import migrateOmegaquotes from './migrateOmegaquotes' import migrateArticles from './migateArticles' import migrateMailAliases from './migrateMailAlias' import migrateEvents from './migrateEvents' +import migrateUsers from './migrateUsers' import { PrismaClient as PrismaClientVeven } from '@/generated/veven' +import manifest from '@/src/logger' import type { PrismaClient as PrismaClientPn } from '@/generated/pn' -import migrateUsers from './migrateUsers' /** * !DobbelOmega! @@ -16,8 +17,8 @@ import migrateUsers from './migrateUsers' * @param pnPrisma - PrismaClientPn */ export default async function dobbelOmega(pnPrisma: PrismaClientPn) { - console.log('============================================') - console.log('==========!!!Dobbel Omega!!!================') + manifest.info('============================================') + manifest.info('==========!!!Dobbel Omega!!!================') const vevenPrisma = new PrismaClientVeven() const limits = getLimits() @@ -32,5 +33,5 @@ export default async function dobbelOmega(pnPrisma: PrismaClientPn) { await migrateEvents(pnPrisma, vevenPrisma, imageIdMap, limits) vevenPrisma.$disconnect() - console.log('=======Dobbel Omega ferdig, dagen derpÄ=======') + manifest.info('=======Dobbel Omega ferdig, dagen derpÄ=======') } diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts index 3a6298dd5..014f96ecd 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts @@ -90,7 +90,7 @@ export default async function migrateImages( if (image.Articles.length) return true if (image.Events.length) return true if (image.collectionId === ombulCollection.id) return true - if (image.collectionId = profileCollection.id) return true + if (image.collectionId === profileCollection.id) return true if (image.ImageGroupId && image.ImageGroupId < limits.numberOffFullImageCollections) return true return false }) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts index c596a4626..5bd595400 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateOmegaquotes.ts @@ -1,8 +1,9 @@ +import { vevenIdToPnId } from './IdMapper' +import logger from '@/src/logger' +import type { IdMapper } from './IdMapper' import type { PrismaClient as PrismaClientPn } from '@/generated/pn' import type { PrismaClient as PrismaClientVeven, Quotes } from '@/generated/veven' import type { Limits } from './migrationLimits' -import { IdMapper, vevenIdToPnId } from './IdMapper' -import logger from '@/src/logger' /** * This function migrates omegaquotes from Veven to PN @@ -41,14 +42,12 @@ export default async function migrateOmegaquotes( }, [] as (Quotes & { PosterId: number })[]) await pnPrisma.omegaQuote.createMany({ - data: omegaquoteFiltered.map(quote => { - return { - quote: quote.quote, - author: quote.author, - timestamp: quote.timestamp || new Date(), - userPosterId: quote.PosterId, - } - }), + data: omegaquoteFiltered.map(quote => ({ + quote: quote.quote, + author: quote.author, + timestamp: quote.timestamp || new Date(), + userPosterId: quote.PosterId, + })), }) //TODO: also seed bulshit into omegaquote diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index 8f7666e25..8b0e17d74 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -1,50 +1,16 @@ import type { PrismaClient as PrismaClientPn, SEX } from '@/generated/pn' -import type { PrismaClient as PrismaClientVeven, enum_Users_sex } from '@/generated/veven' +import type { PrismaClient as PrismaClientVeven, enum_Users_sex as SEXVEVEN } from '@/generated/veven' import type { Limits } from './migrationLimits' import upsertOrderBasedOnDate from './upsertOrderBasedOnDate' import { type IdMapper, vevenIdToPnId } from './IdMapper' -import logger from '@/src/logger' - -function makeEmailUnique( - users: X[] -) : (X & { email: string })[] { - return users.map(user => { - let email = user.email ?? `dobbel@omega.${user.id}.no` - if (!user.email) { - logger.error(`User ${user.id} has no email`) - } else { - // Find users with same email if the email in null it is not a duplicate - const duplicates = users.filter(u => u.email === user.email) - if (duplicates.length > 1) { - logger.error(`User ${user.id} has duplicate email ${user.email}`) - email = `dobbel@omega.${user.id}.no` - } - } - return { ...user, email } - }) -} - -function makeUsernameUnique( - users: X[] -) : (X & { username: string })[] { - return users.map(user => { - let username = user.username - // Find users with same email if the email in null it is not a duplicate - const duplicates = users.filter(u => u.username === user.username) - if (duplicates.length > 1) { - logger.error(`User ${user.id} has duplicate username ${user.username}`) - username = username + user.id - } - return { ...user, username } - }) -} +import manifest from '@/src/logger' /** - * TODO: Need migrate reservations (mail reservations) ?, and flairs + * TODO: Need migrate reservations (mail reservations) ?, and flairs * This function migrates users from Veven to PN. It only migrates to theUser model, not FeideAccounts * or Credentials. These should be linked when people log in for the first time. - * If a user has the soelle field true on veven it will get a relation to the soelle group - * - else it is assumed to be a member and an inactive relation to the soelle group. + * If a user has the soelle field true on veven it will get a relation to the soelle group + * - else it is assumed to be a member and an inactive relation to the soelle group. * i.e. no users are assumed to be external. * @param pnPrisma - PrismaClientPn * @param vevenPrisma - PrismaClientVeven @@ -91,26 +57,30 @@ export default async function migrateUsers( } }) - const yearIdMap = classes.reduce((acc, cur) => { - acc[cur.year] = cur.id - return acc - }, {} as Record) + const yearIdMap = (x: number) => { + const year = classes.find(c => c.year === x) + if (!year) { + manifest.error(`Year ${x} not found - dobbelOmega failed :(`) + throw new Error(`Year ${x} not found`) + } + return year.group.id + } + manifest.info(`Migrating ${users.length} users`) const userIdMap: IdMapper = [] + const sexMap = { + m: 'MALE', + f: 'FEMALE', + other: 'OTHER', + } as const satisfies Record await Promise.all(users.map(async user => { - const sexMap = { - m: "MALE", - f: "FEMALE", - other: "OTHER", - } satisfies Record const pnUser = await pnPrisma.user.create({ data: { - id: user.id, username: user.username, email: user.email, firstname: user.firstname, lastname: user.lastname, - bio: user.bio ?? "", + bio: user.bio ?? '', acceptedTerms: undefined, sex: sexMap[user.sex], allergies: undefined, @@ -124,7 +94,7 @@ export default async function migrateUsers( }) userIdMap.push({ vevenId: user.id, pnId: pnUser.id }) - + // Connect to correct membership group const membershipOrder = await pnPrisma.omegaOrder.upsert({ where: { order: user.order }, @@ -155,206 +125,255 @@ export default async function migrateUsers( } }) } - + // connect to correct class (year) const yearsInProgramme = Math.min(user.StudyProgrammes?.years ?? 0, 5) switch (yearsInProgramme) { - case 0: - logger.error(`User ${user.id} has no years in programme or no programme`) - break - case 2: - //ASSUME 2 years masters. The user can be member of 4 5 and 6 (siving) - let yearOfStudy2 = user.yearOfStudy - if (yearOfStudy2 < 4) { - logger.error(`User ${user.id} is in 2 year programme but has year of study less than 4 - setting to 4`) - yearOfStudy2 = 4 - } - if (yearOfStudy2 > 6) { - logger.error(`User ${user.id} is in 2 year programme but has year of study greater than 6 - setting to 6`) - yearOfStudy2 = 6 - } - if (yearOfStudy2 === 6) { - const orderBecameSiving = user.order + 2 - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[6], - userId: pnUser.id, - active: true, - admin: false, - order: orderBecameSiving, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[5], - userId: pnUser.id, - active: false, - admin: false, - order: orderBecameSiving - 1, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[4], - userId: pnUser.id, - active: false, - admin: false, - order: orderBecameSiving - 2, - } - }) - } else if (yearOfStudy2 == 5) { - const orderBecame5 = user.order + 1 - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[5], - userId: pnUser.id, - active: true, - admin: false, - order: orderBecame5, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[4], - userId: pnUser.id, - active: false, - admin: false, - order: orderBecame5 - 1, - } - }) - } else if (yearOfStudy2 == 4) { - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[4], - userId: pnUser.id, - active: true, - admin: false, - order: user.order, - } - }) - } else { - logger.error(`User ${user.id} is in a 2 year programme but not in year 4, 5 or 6`) - } - break - case 3: - //ASSUME 3 years bachelors. The user can be member of 1 2 and 3, and cannot be siving. - let yearOfStudy3 = user.yearOfStudy - if (yearOfStudy3 < 1) { - logger.error(`User ${user.id} has year of study less than 1 - setting to 1`) - yearOfStudy3 = 1 - } - if (yearOfStudy3 > 3) { - logger.error(`User ${user.id} has year of study greater than 3 - setting to 3`) - yearOfStudy3 = 3 + case 0: + manifest.error(`User ${user.id} has no years in programme or no programme`) + break + case 2: + { + //ASSUME 2 years masters. The user can be member of 4 5 and 6 (siving) + let yearOfStudy2 = user.yearOfStudy + if (yearOfStudy2 < 4) { + manifest.error( + `User ${user.id} is in 2 year programme but has year of study less than 4 - setting to 4` + ) + yearOfStudy2 = 4 + } + if (yearOfStudy2 > 6) { + manifest.error( + `User ${user.id} is in 2 year programme but has year of study greater than 6 - setting to 6` + ) + yearOfStudy2 = 6 + } + if (yearOfStudy2 === 6) { + const orderBecameSiving = user.order + 2 + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(6), + userId: pnUser.id, + active: true, + admin: false, + order: orderBecameSiving, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(5), + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - 1, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(4), + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - 2, + } + }) + } else if (yearOfStudy2 === 5) { + const orderBecame5 = user.order + 1 + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(5), + userId: pnUser.id, + active: true, + admin: false, + order: orderBecame5, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(4), + userId: pnUser.id, + active: false, + admin: false, + order: orderBecame5 - 1, + } + }) + } else if (yearOfStudy2 === 4) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(4), + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) + } else { + manifest.error(`User ${user.id} is in a 2 year programme but not in year 4, 5 or 6`) + } + break } - if (yearOfStudy3 === 1) { - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[1], - userId: pnUser.id, - active: true, - admin: false, - order: user.order, - } - }) - } else if (yearOfStudy3 === 2) { - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[2], - userId: pnUser.id, - active: true, - admin: false, - order: user.order, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[1], - userId: pnUser.id, - active: false, - admin: false, - order: user.order - 1, - } - }) - } else if (yearOfStudy3 === 3) { + case 3: + { + //ASSUME 3 years bachelors. The user can be member of 1 2 and 3, and cannot be siving. + let yearOfStudy3 = user.yearOfStudy + if (yearOfStudy3 < 1) { + manifest.error(`User ${user.id} has year of study less than 1 - setting to 1`) + yearOfStudy3 = 1 + } + if (yearOfStudy3 > 3) { + manifest.error(`User ${user.id} has year of study greater than 3 - setting to 3`) + yearOfStudy3 = 3 + } + if (yearOfStudy3 === 1) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(1), + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) + } else if (yearOfStudy3 === 2) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(2), + userId: pnUser.id, + active: true, + admin: false, + order: user.order, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(1), + userId: pnUser.id, + active: false, + admin: false, + order: user.order - 1, + } + }) + } else if (yearOfStudy3 === 3) { // This is a nut - it is hard to say if the user still is in 3. grade. // We will assume that the user is in 3. grade if the order is gte 103 - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[3], - userId: pnUser.id, - active: user.order >= 103, - admin: false, - order: user.order, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[2], - userId: pnUser.id, - active: false, - admin: false, - order: user.order - 1, - } - }) - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[1], - userId: pnUser.id, - active: false, - admin: false, - order: user.order - 2, - } - }) - } - break - case 5: - // Assuming 5 year masters. The user can be member of 1 2 3 4 5 and 6 (siving) - let yearOfStudy5 = user.yearOfStudy - if (yearOfStudy5 < 1) { - logger.error(`User ${user.id} is in 5 year programme but has year of study less than 1 - setting to 1`) - yearOfStudy5 = 1 - } - if (yearOfStudy5 > 6) { - logger.error(`User ${user.id} is in 5 year programme but has year of study greater than 6 - setting to 6`) - yearOfStudy5 = 6 - } - if (yearOfStudy5 === 6) { - const orderBecameSiving = user.order + 5 // Assume the user used 5 years to get to sivin - await pnPrisma.membership.create({ - data: { - groupId: yearIdMap[6], - userId: pnUser.id, - active: true, - admin: false, - order: orderBecameSiving, - } - }) - for (let i = 5; i >= 1; i--) { await pnPrisma.membership.create({ data: { - groupId: yearIdMap[i], + groupId: yearIdMap(3), + userId: pnUser.id, + active: user.order >= 103, + admin: false, + order: user.order, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(2), userId: pnUser.id, active: false, admin: false, - order: orderBecameSiving - i, + order: user.order - 1, + } + }) + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(1), + userId: pnUser.id, + active: false, + admin: false, + order: user.order - 2, } }) } - } else { - for (let year=1; year <= yearOfStudy5; year++) { + break + } + case 5: + { + // Assuming 5 year masters. The user can be member of 1 2 3 4 5 and 6 (siving) + let yearOfStudy5 = user.yearOfStudy + if (yearOfStudy5 < 1) { + manifest.error(`User ${user.id} is in 5 year programme but has year of study less than 1 - setting to 1`) + yearOfStudy5 = 1 + } + if (yearOfStudy5 > 6) { + manifest.error( + `User ${user.id} is in 5 year programme but has year of study greater than 6 - setting to 6` + ) + yearOfStudy5 = 6 + } + if (yearOfStudy5 === 6) { + const orderBecameSiving = user.order + 5 // Assume the user used 5 years to get to sivin await pnPrisma.membership.create({ data: { - groupId: yearIdMap[year], + groupId: yearIdMap(6), userId: pnUser.id, - active: year === user.yearOfStudy, + active: true, admin: false, - order: user.order + year - 1, + order: orderBecameSiving, } }) + for (let i = 5; i >= 1; i--) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(i), + userId: pnUser.id, + active: false, + admin: false, + order: orderBecameSiving - (6 - i), + } + }) + } + } else { + for (let year = 1; year <= yearOfStudy5; year++) { + await pnPrisma.membership.create({ + data: { + groupId: yearIdMap(year), + userId: pnUser.id, + active: year === yearOfStudy5, + admin: false, + order: user.order + year - 1, + } + }) + } } + break } + default: + manifest.error(`User ${user.id} has ${yearsInProgramme} years in programme - dobbelOmega failed :(`) } })) return userIdMap -} \ No newline at end of file +} + +function makeEmailUnique( + users: X[] +): (X & { email: string })[] { + return users.map(user => { + let email = user.email ?? `dobbel@omega.${user.id}.no` + if (!user.email) { + manifest.error(`User ${user.id} has no email`) + } else { + // Find users with same email if the email in null it is not a duplicate + const duplicates = users.filter(u => u.email === user.email) + if (duplicates.length > 1) { + manifest.error(`User ${user.id} has duplicate email ${user.email}`) + email = `dobbel@omega.${user.id}.no` + } + } + return { ...user, email } + }) +} + +function makeUsernameUnique( + users: X[] +): (X & { username: string })[] { + return users.map(user => { + let username = user.username + // Find users with same email if the email in null it is not a duplicate + const duplicates = users.filter(u => u.username === user.username) + if (duplicates.length > 1) { + manifest.error(`User ${user.id} has duplicate username ${user.username}`) + username = username + user.id + } + return { ...user, username } + }) +} diff --git a/src/prisma/prismaservice/src/logger/index.ts b/src/prisma/prismaservice/src/logger/index.ts index 854676d7c..83fc5b5c2 100644 --- a/src/prisma/prismaservice/src/logger/index.ts +++ b/src/prisma/prismaservice/src/logger/index.ts @@ -1,14 +1,17 @@ import winston from 'winston' -const logger = winston.createLogger({ +/** + * Logger for the manifest the dobbelOmega migration + */ +const manifest = winston.createLogger({ level: 'silly', }) -logger.add(new winston.transports.Console()) +manifest.add(new winston.transports.Console()) -logger.add(new winston.transports.File({ +manifest.add(new winston.transports.File({ filename: 'manifest.log', dirname: 'dobbelOmegaManifest', })) -export default logger \ No newline at end of file +export default manifest diff --git a/src/prisma/prismaservice/src/seedClasses.ts b/src/prisma/prismaservice/src/seedClasses.ts index af6587505..8d62e6858 100644 --- a/src/prisma/prismaservice/src/seedClasses.ts +++ b/src/prisma/prismaservice/src/seedClasses.ts @@ -9,7 +9,7 @@ export default async function seedClasses(prisma: PrismaClient) { if (!omegaOrder) throw new Error('No omega order found') const order = omegaOrder.order - await Promise.all([0, 1, 2, 3, 4, 5, 6].map(i => prisma.class.create({ + await Promise.all([1, 2, 3, 4, 5, 6].map(i => prisma.class.create({ data: { year: i, group: { From df392c8aa5d9b8def0fed173d54fd70bdc0104ba Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:57:29 +0200 Subject: [PATCH 09/10] chore: adds batched image upload --- .../src/dobbelOmega/migrateImages.ts | 102 ++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts index 014f96ecd..af5551dea 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts @@ -1,4 +1,5 @@ import { vevenIdToPnId, type IdMapper } from './IdMapper' +import manifest from '@/src/logger' import { imageSizes, imageStoreLocation } from '@/src/seedImages' import { v4 as uuid } from 'uuid' import { writeFile, mkdir } from 'fs/promises' @@ -70,14 +71,22 @@ export default async function migrateImages( } }) + // Find what the profile collection is on veven + const vevenProfileCollection = await vevenPrisma.imageGroups.findFirstOrThrow({ + where: { + name: 'Profilbilder', + }, + }) + + manifest.info(`Before filter: ${images.length} images`) const imagesWithCollection = images.map(image => { let collectionId = vevenIdToPnId(migrateImageCollectionIdMap, image.ImageGroupId) if (image.Ombul.length) { collectionId = ombulCollection.id - } else if (image.UserId) { - collectionId = profileCollection.id } else if (!collectionId) { collectionId = garbageCollection.id + } else if (image.ImageGroupId === vevenProfileCollection.id) { + collectionId = profileCollection.id } return { ...image, @@ -85,7 +94,7 @@ export default async function migrateImages( } }).filter(image => { //Apply limits - if (!limits.numberOffFullImageCollections) return true + if (limits.numberOffFullImageCollections === null) return true if (image.Ombul.length) return true if (image.Articles.length) return true if (image.Events.length) return true @@ -94,36 +103,9 @@ export default async function migrateImages( if (image.ImageGroupId && image.ImageGroupId < limits.numberOffFullImageCollections) return true return false }) + manifest.info(`After filter: ${imagesWithCollection.length} images`) - const imagesWithCollectionAndFs = await Promise.all(imagesWithCollection.map(async (image) => { - const ext = image.originalName.split('.').pop() || '' - const fsLocationDefaultOldVev = `${process.env.VEVEN_STORE_URL}/image/default/${image.name}` - + `?url=/store/images/${image.name}.${ext}` - const fsLocationMediumOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.medium}/` - + `${imageSizes.medium}/${image.name}?url=/store/images/${image.name}.${ext}` - const fsLocationSmallOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.small}/` - + `${imageSizes.small}/${image.name}?url=/store/images/${image.name}.${ext}` - const fsLocationLargeOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.large}/` - + `${imageSizes.large}/${image.name}?url=/store/images/${image.name}.${ext}` - const [fsLocationSmallSize, fsLocationMediumSize, fsLocationLargeSize, fsLocationOriginal] = await Promise.all([ - fetchImageAndUploadToStore(fsLocationSmallOldVev), - fetchImageAndUploadToStore(fsLocationMediumOldVev), - fetchImageAndUploadToStore(fsLocationLargeOldVev), - fetchImageAndUploadToStore(fsLocationDefaultOldVev), - ]) - if (!fsLocationOriginal || !fsLocationMediumSize || !fsLocationSmallSize || !fsLocationLargeSize) { - console.error(`Failed to fetch image from ${fsLocationDefaultOldVev}`) - return null - } - - return { - ...image, - fsLocationSmallSize, - fsLocationMediumSize, - fsLocationLargeSize, - fsLocationOriginal, - } - })) + const imagesWithCollectionAndFs = await fetchAllImagesAndUploadToStore(imagesWithCollection) //correct names if there are duplicates const namesTaken: { name: string, times: number }[] = [] @@ -178,6 +160,62 @@ export default async function migrateImages( return migrateImageIdMap } +type Locations = { + fsLocationOriginal: string, + fsLocationSmallSize: string, + fsLocationMediumSize: string, + fsLocationLargeSize: string +} + +async function fetchAllImagesAndUploadToStore(images: X[]): Promise<((X & Locations) | null)[]> { + const ret: ((X & Locations) | null)[] = [] + let imageCounter = 1 + const batchSize = 1200 + const imageBatches = images.reduce((acc, image) => { + if (acc[acc.length - 1].length >= batchSize) { + acc.push([image]) + } else { + acc[acc.length - 1].push(image) + } + return acc + }, [[]] as X[][]) + for (const imageBatch of imageBatches) { + await Promise.all(imageBatch.map(async image => { + manifest.info(`Migrating image number ${imageCounter++} of ${images.length}`) + const ext = image.originalName.split('.').pop() || '' + const fsLocationDefaultOldVev = `${process.env.VEVEN_STORE_URL}/image/default/${image.name}` + + `?url=/store/images/${image.name}.${ext}` + const fsLocationMediumOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.medium}/` + + `${imageSizes.medium}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationSmallOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.small}/` + + `${imageSizes.small}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationLargeOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.large}/` + + `${imageSizes.large}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationOriginal = await fetchImageAndUploadToStore(fsLocationDefaultOldVev) + const fsLocationMediumSize = await fetchImageAndUploadToStore(fsLocationMediumOldVev) + const fsLocationSmallSize = await fetchImageAndUploadToStore(fsLocationSmallOldVev) + const fsLocationLargeSize = await fetchImageAndUploadToStore(fsLocationLargeOldVev) + if (!fsLocationOriginal || !fsLocationMediumSize || !fsLocationSmallSize || !fsLocationLargeSize) { + console.error(`Failed to fetch image from ${fsLocationDefaultOldVev}`) + ret.push(null) + } else { + ret.push({ + ...image, + fsLocationSmallSize, + fsLocationMediumSize, + fsLocationOriginal, + fsLocationLargeSize + }) + } + })) + } + return ret +} + /** * fetches an image from the Veven store and uploads it to the PN store (does NOT store the image * in the database, only the file on the server). Note that veven will often fail when we make a From 2b45305fc8a9fc24c198b8ebca1a46d422161506 Mon Sep 17 00:00:00 2001 From: JohanHjelsethStorstad <82723971+JohanHjelsethStorstad@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:49:54 +0100 Subject: [PATCH 10/10] style: fix linter --- .../src/dobbelOmega/migrateImages.ts | 66 ++++++++++--------- .../src/dobbelOmega/migrateUsers.ts | 6 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts index af5551dea..6fcb72123 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateImages.ts @@ -160,9 +160,9 @@ export default async function migrateImages( return migrateImageIdMap } -type Locations = { - fsLocationOriginal: string, - fsLocationSmallSize: string, +type Locations = { + fsLocationOriginal: string, + fsLocationSmallSize: string, fsLocationMediumSize: string, fsLocationLargeSize: string } @@ -183,35 +183,39 @@ async function fetchAllImagesAndUploadToStore { + manifest.info(`Migrating image number ${imageCounter++} of ${images.length}`) + const ext = image.originalName.split('.').pop() || '' + const fsLocationDefaultOldVev = `${process.env.VEVEN_STORE_URL}/image/default/${image.name}` + + `?url=/store/images/${image.name}.${ext}` + const fsLocationMediumOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.medium}/` + + `${imageSizes.medium}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationSmallOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.small}/` + + `${imageSizes.small}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationLargeOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.large}/` + + `${imageSizes.large}/${image.name}?url=/store/images/${image.name}.${ext}` + const fsLocationOriginal = await fetchImageAndUploadToStore(fsLocationDefaultOldVev) + const fsLocationMediumSize = await fetchImageAndUploadToStore(fsLocationMediumOldVev) + const fsLocationSmallSize = await fetchImageAndUploadToStore(fsLocationSmallOldVev) + const fsLocationLargeSize = await fetchImageAndUploadToStore(fsLocationLargeOldVev) + if (!fsLocationOriginal || !fsLocationMediumSize || !fsLocationSmallSize || !fsLocationLargeSize) { + console.error(`Failed to fetch image from ${fsLocationDefaultOldVev}`) + ret.push(null) + } else { + ret.push({ + ...image, + fsLocationSmallSize, + fsLocationMediumSize, + fsLocationOriginal, + fsLocationLargeSize + }) + } + } + + for (const imageBatch of imageBatches) { - await Promise.all(imageBatch.map(async image => { - manifest.info(`Migrating image number ${imageCounter++} of ${images.length}`) - const ext = image.originalName.split('.').pop() || '' - const fsLocationDefaultOldVev = `${process.env.VEVEN_STORE_URL}/image/default/${image.name}` - + `?url=/store/images/${image.name}.${ext}` - const fsLocationMediumOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.medium}/` - + `${imageSizes.medium}/${image.name}?url=/store/images/${image.name}.${ext}` - const fsLocationSmallOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.small}/` - + `${imageSizes.small}/${image.name}?url=/store/images/${image.name}.${ext}` - const fsLocationLargeOldVev = `${process.env.VEVEN_STORE_URL}/image/resize/${imageSizes.large}/` - + `${imageSizes.large}/${image.name}?url=/store/images/${image.name}.${ext}` - const fsLocationOriginal = await fetchImageAndUploadToStore(fsLocationDefaultOldVev) - const fsLocationMediumSize = await fetchImageAndUploadToStore(fsLocationMediumOldVev) - const fsLocationSmallSize = await fetchImageAndUploadToStore(fsLocationSmallOldVev) - const fsLocationLargeSize = await fetchImageAndUploadToStore(fsLocationLargeOldVev) - if (!fsLocationOriginal || !fsLocationMediumSize || !fsLocationSmallSize || !fsLocationLargeSize) { - console.error(`Failed to fetch image from ${fsLocationDefaultOldVev}`) - ret.push(null) - } else { - ret.push({ - ...image, - fsLocationSmallSize, - fsLocationMediumSize, - fsLocationOriginal, - fsLocationLargeSize - }) - } - })) + await Promise.all(imageBatch.map(uploadOne)) } return ret } diff --git a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts index 8b0e17d74..94b3be46f 100644 --- a/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts +++ b/src/prisma/prismaservice/src/dobbelOmega/migrateUsers.ts @@ -1,9 +1,9 @@ -import type { PrismaClient as PrismaClientPn, SEX } from '@/generated/pn' -import type { PrismaClient as PrismaClientVeven, enum_Users_sex as SEXVEVEN } from '@/generated/veven' -import type { Limits } from './migrationLimits' import upsertOrderBasedOnDate from './upsertOrderBasedOnDate' import { type IdMapper, vevenIdToPnId } from './IdMapper' import manifest from '@/src/logger' +import type { PrismaClient as PrismaClientPn, SEX } from '@/generated/pn' +import type { PrismaClient as PrismaClientVeven, enum_Users_sex as SEXVEVEN } from '@/generated/veven' +import type { Limits } from './migrationLimits' /** * TODO: Need migrate reservations (mail reservations) ?, and flairs