From 803e4b67046d3ac4f1d7a8bc09ce3a943f559f2a Mon Sep 17 00:00:00 2001 From: juancarmore Date: Sun, 22 Feb 2026 13:42:37 +0100 Subject: [PATCH] backend: enhance migration registries and improve type handling in migration service --- .../src/migrations/migration-registry.ts | 123 ++++++++++-------- .../backend/src/migrations/user-migrations.ts | 2 +- .../backend/src/services/migration.service.ts | 19 ++- 3 files changed, 81 insertions(+), 63 deletions(-) diff --git a/meet-ce/backend/src/migrations/migration-registry.ts b/meet-ce/backend/src/migrations/migration-registry.ts index 1480ffd1..d18280f0 100644 --- a/meet-ce/backend/src/migrations/migration-registry.ts +++ b/meet-ce/backend/src/migrations/migration-registry.ts @@ -30,62 +30,75 @@ import { roomMigrations } from './room-migrations.js'; import { userMigrations } from './user-migrations.js'; /** - * Central registry of all collection migrations. - * Defines the current version and migration map for each collection. - * - * Order matters: collections should be listed in dependency order. - * For example, if recordings depend on rooms, rooms should come first. + * Registry configuration for MeetGlobalConfig collection migrations. */ -const migrationRegistry: [ - CollectionMigrationRegistry, - CollectionMigrationRegistry, - CollectionMigrationRegistry, - CollectionMigrationRegistry, - CollectionMigrationRegistry, - CollectionMigrationRegistry -] = [ - // GlobalConfig - no dependencies, can run first - { - collectionName: meetGlobalConfigCollectionName, - model: MeetGlobalConfigModel, - currentVersion: INTERNAL_CONFIG.GLOBAL_CONFIG_SCHEMA_VERSION, - migrations: globalConfigMigrations - }, - // User - no dependencies - { - collectionName: meetUserCollectionName, - model: MeetUserModel, - currentVersion: INTERNAL_CONFIG.USER_SCHEMA_VERSION, - migrations: userMigrations - }, - // ApiKey - no dependencies - { - collectionName: meetApiKeyCollectionName, - model: MeetApiKeyModel, - currentVersion: INTERNAL_CONFIG.API_KEY_SCHEMA_VERSION, - migrations: apiKeyMigrations - }, - // Room - no dependencies on other collections - { - collectionName: meetRoomCollectionName, - model: MeetRoomModel, - currentVersion: INTERNAL_CONFIG.ROOM_SCHEMA_VERSION, - migrations: roomMigrations - }, - // RoomMember - depends on Room (references roomId) - { - collectionName: meetRoomMemberCollectionName, - model: MeetRoomMemberModel, - currentVersion: INTERNAL_CONFIG.ROOM_MEMBER_SCHEMA_VERSION, - migrations: roomMemberMigrations - }, - // Recording - depends on Room (references roomId) - { - collectionName: meetRecordingCollectionName, - model: MeetRecordingModel, - currentVersion: INTERNAL_CONFIG.RECORDING_SCHEMA_VERSION, - migrations: recordingMigrations - } +const globalConfigMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetGlobalConfigCollectionName, + model: MeetGlobalConfigModel, + currentVersion: INTERNAL_CONFIG.GLOBAL_CONFIG_SCHEMA_VERSION, + migrations: globalConfigMigrations +}; + +/** + * Registry configuration for MeetApiKey collection migrations. + */ +const apiKeyMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetApiKeyCollectionName, + model: MeetApiKeyModel, + currentVersion: INTERNAL_CONFIG.API_KEY_SCHEMA_VERSION, + migrations: apiKeyMigrations +}; + +/** + * Registry configuration for MeetUser collection migrations. + */ +const userMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetUserCollectionName, + model: MeetUserModel, + currentVersion: INTERNAL_CONFIG.USER_SCHEMA_VERSION, + migrations: userMigrations +}; + +/** + * Registry configuration for MeetRoom collection migrations. + */ +const roomMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetRoomCollectionName, + model: MeetRoomModel, + currentVersion: INTERNAL_CONFIG.ROOM_SCHEMA_VERSION, + migrations: roomMigrations +}; + +/** + * Registry configuration for MeetRoomMember collection migrations. + */ +const roomMemberMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetRoomMemberCollectionName, + model: MeetRoomMemberModel, + currentVersion: INTERNAL_CONFIG.ROOM_MEMBER_SCHEMA_VERSION, + migrations: roomMemberMigrations +}; + +/** + * Registry configuration for MeetRecording collection migrations. + */ +const recordingMigrationRegistry: CollectionMigrationRegistry = { + collectionName: meetRecordingCollectionName, + model: MeetRecordingModel, + currentVersion: INTERNAL_CONFIG.RECORDING_SCHEMA_VERSION, + migrations: recordingMigrations +}; + +/** + * Central registry of all collection migrations. + */ +const migrationRegistry = [ + globalConfigMigrationRegistry, + apiKeyMigrationRegistry, + userMigrationRegistry, + roomMigrationRegistry, + roomMemberMigrationRegistry, + recordingMigrationRegistry ]; /** diff --git a/meet-ce/backend/src/migrations/user-migrations.ts b/meet-ce/backend/src/migrations/user-migrations.ts index c0198004..c441cd62 100644 --- a/meet-ce/backend/src/migrations/user-migrations.ts +++ b/meet-ce/backend/src/migrations/user-migrations.ts @@ -7,7 +7,7 @@ const userMigrationV1ToV2Name = generateSchemaMigrationName(meetUserCollectionNa const userMigrationV1ToV2Transform: SchemaTransform = (user: MeetUserDocument) => { const legacyUser = user as unknown as { username?: string; - roles: unknown; + roles?: unknown; }; // NOTE: This migration assumes that there is only one user in the system, diff --git a/meet-ce/backend/src/services/migration.service.ts b/meet-ce/backend/src/services/migration.service.ts index b64fc48b..bf34d859 100644 --- a/meet-ce/backend/src/services/migration.service.ts +++ b/meet-ce/backend/src/services/migration.service.ts @@ -1,5 +1,5 @@ import { inject, injectable } from 'inversify'; -import { Model } from 'mongoose'; +import { FilterQuery, Model, Require_id, Types } from 'mongoose'; import ms from 'ms'; import { MeetLock } from '../helpers/redis.helper.js'; import { runtimeMigrationRegistry } from '../migrations/migration-registry.js'; @@ -270,7 +270,7 @@ export class MigrationService { let migratedCount = 0; let failedCount = 0; - const sourceVersionFilter = { schemaVersion: sourceSchemaVersion }; + const sourceVersionFilter: FilterQuery = { schemaVersion: sourceSchemaVersion }; const totalSourceVersionDocuments = await model.countDocuments(sourceVersionFilter).exec(); if (totalSourceVersionDocuments === 0) { @@ -282,18 +282,23 @@ export class MigrationService { } let processedDocumentsCount = 0; - let lastProcessedDocumentId: TDocument['_id'] | null = null; + let lastProcessedDocumentId: Types.ObjectId | null = null; let hasMoreBatches = true; while (hasMoreBatches) { - const batchFilter = + const batchFilter: FilterQuery = lastProcessedDocumentId === null ? sourceVersionFilter : { ...sourceVersionFilter, _id: { $gt: lastProcessedDocumentId } }; - const documents = await model.find(batchFilter).sort({ _id: 1 }).limit(batchSize).exec(); + const documents = (await model + .find(batchFilter) + .sort({ _id: 1 }) + .limit(batchSize) + .lean() + .exec()) as Require_id[]; if (documents.length === 0) { break; @@ -302,8 +307,8 @@ export class MigrationService { const batchResults = await Promise.allSettled( documents.map(async (doc) => { const migratedDocument = this.applyTransformChain(doc, migrationChain, targetVersion); - await migratedDocument.save(); - return String(doc._id); + await model.replaceOne({ _id: doc._id }, migratedDocument).exec(); + return doc._id; }) );