From 8f7a4c40b4cbc9dabbb002049136fbb2691f38f9 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Mon, 13 Oct 2025 16:16:34 +0200 Subject: [PATCH] backend: add migration for authTransportMode in global config --- .../src/services/storage/storage.service.ts | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/backend/src/services/storage/storage.service.ts b/backend/src/services/storage/storage.service.ts index 0746726..4708174 100644 --- a/backend/src/services/storage/storage.service.ts +++ b/backend/src/services/storage/storage.service.ts @@ -107,6 +107,7 @@ export class MeetStorageService< /** * Initializes the storage with default data and initial environment variables if not already initialized. * This includes global config, admin user and API key. + * Also runs migrations to update existing data structures when needed. */ async initializeStorage(): Promise { try { @@ -123,7 +124,9 @@ export class MeetStorageService< const isInitialized = await this.checkStorageInitialization(); if (isInitialized) { - this.logger.verbose('Storage already initialized for this project, skipping initialization'); + this.logger.verbose('Storage already initialized for this project, running migrations if needed'); + // Run migrations to update existing data structures + await this.runMigrations(); return; } @@ -776,6 +779,74 @@ export class MeetStorageService< this.logger.info('API key initialized'); } + /** + * Runs all necessary migrations to update existing data structures. + * This method should be called during startup to ensure backwards compatibility + * when new fields are added to existing data structures. + */ + protected async runMigrations(): Promise { + this.logger.info('Running storage migrations...'); + + try { + await this.migrateGlobalConfigAuthTransportMode(); + this.logger.info('All migrations completed successfully'); + } catch (error) { + this.logger.error('Error running migrations:', error); + throw error; + } + } + + /** + * Migration: Adds authTransportMode field to existing global config if missing. + * This migration ensures backwards compatibility when upgrading from versions + * that didn't have the authTransportMode field. + */ + protected async migrateGlobalConfigAuthTransportMode(): Promise { + try { + const redisKey = RedisKeyName.GLOBAL_CONFIG; + const storageKey = this.keyBuilder.buildGlobalConfigKey(); + + // Get current config directly from storage + const currentConfig = await this.getFromCacheAndStorage(redisKey, storageKey); + + if (!currentConfig) { + this.logger.debug('No global config found, skipping authTransportMode migration'); + return; + } + + // Check if authTransportMode is missing + const authConfig = currentConfig.securityConfig?.authentication; + + if (!authConfig) { + this.logger.warn('Authentication config missing in global config, skipping migration'); + return; + } + + // Check if the property already exists + if ('authTransportMode' in authConfig) { + this.logger.debug('authTransportMode already exists, skipping migration'); + return; + } + + this.logger.info('Migrating global config: adding authTransportMode field'); + + // Directly add the missing field to the existing object + Object.assign(currentConfig.securityConfig.authentication, { + authTransportMode: AuthTransportMode.HEADER + }); + + // Save updated config + await this.saveCacheAndStorage(redisKey, storageKey, currentConfig); + + this.logger.info( + 'Successfully migrated global config: authTransportMode added with default value "header"' + ); + } catch (error) { + this.logger.error('Error migrating authTransportMode in global config:', error); + throw error; + } + } + /** * Normalizes room data for storage by ensuring URLs contain only paths * @param room - The room object to normalize