backend: enhance migration registries and improve type handling in migration service

This commit is contained in:
juancarmore 2026-02-22 13:42:37 +01:00
parent 696df4a7a2
commit 803e4b6704
3 changed files with 81 additions and 63 deletions

View File

@ -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<MeetGlobalConfigDocument>,
CollectionMigrationRegistry<MeetUserDocument>,
CollectionMigrationRegistry<MeetApiKeyDocument>,
CollectionMigrationRegistry<MeetRoomDocument>,
CollectionMigrationRegistry<MeetRoomMemberDocument>,
CollectionMigrationRegistry<MeetRecordingDocument>
] = [
// 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<MeetGlobalConfigDocument> = {
collectionName: meetGlobalConfigCollectionName,
model: MeetGlobalConfigModel,
currentVersion: INTERNAL_CONFIG.GLOBAL_CONFIG_SCHEMA_VERSION,
migrations: globalConfigMigrations
};
/**
* Registry configuration for MeetApiKey collection migrations.
*/
const apiKeyMigrationRegistry: CollectionMigrationRegistry<MeetApiKeyDocument> = {
collectionName: meetApiKeyCollectionName,
model: MeetApiKeyModel,
currentVersion: INTERNAL_CONFIG.API_KEY_SCHEMA_VERSION,
migrations: apiKeyMigrations
};
/**
* Registry configuration for MeetUser collection migrations.
*/
const userMigrationRegistry: CollectionMigrationRegistry<MeetUserDocument> = {
collectionName: meetUserCollectionName,
model: MeetUserModel,
currentVersion: INTERNAL_CONFIG.USER_SCHEMA_VERSION,
migrations: userMigrations
};
/**
* Registry configuration for MeetRoom collection migrations.
*/
const roomMigrationRegistry: CollectionMigrationRegistry<MeetRoomDocument> = {
collectionName: meetRoomCollectionName,
model: MeetRoomModel,
currentVersion: INTERNAL_CONFIG.ROOM_SCHEMA_VERSION,
migrations: roomMigrations
};
/**
* Registry configuration for MeetRoomMember collection migrations.
*/
const roomMemberMigrationRegistry: CollectionMigrationRegistry<MeetRoomMemberDocument> = {
collectionName: meetRoomMemberCollectionName,
model: MeetRoomMemberModel,
currentVersion: INTERNAL_CONFIG.ROOM_MEMBER_SCHEMA_VERSION,
migrations: roomMemberMigrations
};
/**
* Registry configuration for MeetRecording collection migrations.
*/
const recordingMigrationRegistry: CollectionMigrationRegistry<MeetRecordingDocument> = {
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
];
/**

View File

@ -7,7 +7,7 @@ const userMigrationV1ToV2Name = generateSchemaMigrationName(meetUserCollectionNa
const userMigrationV1ToV2Transform: SchemaTransform<MeetUserDocument> = (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,

View File

@ -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<TDocument> = { 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<TDocument> =
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<TDocument>[];
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;
})
);