// syncDatabase.js import { initializeDatabase, syncModelsWithUpdates, syncModelsAlways, sequelize } from './sequelize.js'; import initializeTypes from './initializeTypes.js'; import initializeSettings from './initializeSettings.js'; import initializeUserRights from './initializeUserRights.js'; import initializeImageTypes from './initializeImageTypes.js'; import initializeFalukant from './initializeFalukant.js'; import setupAssociations from '../models/associations.js'; import models from '../models/index.js'; import { createTriggers } from '../models/trigger.js'; import initializeForum from './initializeForum.js'; import initializeChat from './initializeChat.js'; import initializeMatch3Data from './initializeMatch3.js'; import updateExistingMatch3Levels from './updateExistingMatch3Levels.js'; import initializeTaxi from './initializeTaxi.js'; // Normale Synchronisation (nur bei STAGE=dev Schema-Updates) const syncDatabase = async () => { try { // Zeige den aktuellen Stage an const currentStage = process.env.STAGE || 'nicht gesetzt'; console.log(`🚀 Starte Datenbank-Synchronisation (Stage: ${currentStage})`); if (currentStage !== 'dev') { console.log('⚠️ WARNUNG: Automatische Schema-Updates sind deaktiviert'); console.log('💡 Setze STAGE=dev in der .env Datei für automatische Schema-Updates'); console.log('🔒 Produktionsmodus: Nur normale Synchronisation ohne Schema-Änderungen'); } else { console.log('✅ Entwicklungsmodus aktiv - Schema-Updates sind aktiviert'); } console.log("Initializing database schemas..."); await initializeDatabase(); // Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt console.log("Pre-ensure Taxi columns (traffic_light) ..."); try { await sequelize.query(` DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'taxi' AND table_name = 'taxi_map_tile' AND column_name = 'traffic_light' ) THEN ALTER TABLE taxi.taxi_map_tile ADD COLUMN traffic_light BOOLEAN NOT NULL DEFAULT false; END IF; END $$; `); console.log("✅ traffic_light-Spalte ist vorhanden"); } catch (e) { console.warn('⚠️ Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e); } // Cleanup: Entferne verwaiste Einträge vor Schema-Updates (nur wenn Schema-Updates aktiviert) if (currentStage === 'dev') { console.log("Cleaning up orphaned entries..."); try { // Cleanup user_param_visibility const result1 = await sequelize.query(` DELETE FROM community.user_param_visibility WHERE param_id NOT IN ( SELECT id FROM community.user_param ); `); const deletedCount1 = result1[1] || 0; if (deletedCount1 > 0) { console.log(`✅ ${deletedCount1} verwaiste user_param_visibility Einträge entfernt`); } // Cleanup stock mit ungültigen branch_id (0 oder nicht existierend) const result2 = await sequelize.query(` DELETE FROM falukant_data.stock WHERE branch_id = 0 OR branch_id NOT IN ( SELECT id FROM falukant_data.branch ); `); const deletedCount2 = result2[1] || 0; if (deletedCount2 > 0) { console.log(`✅ ${deletedCount2} verwaiste stock Einträge entfernt`); } // Cleanup knowledge mit ungültigen character_id oder product_id const result3 = await sequelize.query(` DELETE FROM falukant_data.knowledge WHERE character_id NOT IN ( SELECT id FROM falukant_data.character ) OR product_id NOT IN ( SELECT id FROM falukant_type.product ); `); const deletedCount3 = result3[1] || 0; if (deletedCount3 > 0) { console.log(`✅ ${deletedCount3} verwaiste knowledge Einträge entfernt`); } if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0) { console.log("✅ Keine verwaisten Einträge gefunden"); } } catch (e) { console.warn('⚠️ Konnte verwaiste Einträge nicht bereinigen:', e?.message || e); } } console.log("Setting up associations..."); setupAssociations(); console.log("Synchronizing models..."); await syncModelsWithUpdates(models); console.log("Initializing settings..."); await initializeSettings(); console.log("Initializing types..."); await initializeTypes(); console.log("Initializing user rights..."); await initializeUserRights(); console.log("Initializing image types..."); await initializeImageTypes(); console.log("Initializing forums..."); await initializeForum(); console.log("Initializing Falukant..."); await initializeFalukant(); console.log("Creating triggers..."); await createTriggers(); console.log("Initializing chat..."); await initializeChat(); // Match3-Initialisierung NACH der Model-Synchronisation console.log("Initializing Match3..."); await initializeMatch3Data(); // Match3-Levels aktualisieren NACH der Initialisierung console.log("Updating existing Match3 levels..."); await updateExistingMatch3Levels(); console.log("Initializing Taxi..."); await initializeTaxi(); console.log('Database synchronization complete.'); } catch (error) { console.error('Unable to synchronize the database:', error); } }; // Deployment-Synchronisation (immer Schema-Updates) const syncDatabaseForDeployment = async () => { try { // Zeige den aktuellen Stage an const currentStage = process.env.STAGE || 'nicht gesetzt'; console.log(`🚀 Starte Datenbank-Synchronisation für Deployment (Stage: ${currentStage})`); console.log('✅ Deployment-Modus: Schema-Updates sind immer aktiviert'); console.log("Initializing database schemas..."); await initializeDatabase(); // Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt console.log("Pre-ensure Taxi columns (traffic_light) ..."); try { await sequelize.query(` DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'taxi' AND table_name = 'taxi_map_tile' AND column_name = 'traffic_light' ) THEN ALTER TABLE taxi.taxi_map_tile ADD COLUMN traffic_light BOOLEAN NOT NULL DEFAULT false; END IF; END $$; `); console.log("✅ traffic_light-Spalte ist vorhanden"); } catch (e) { console.warn('⚠️ Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e); } // Migration: Transport product_id und size nullable machen console.log("Making transport product_id and size nullable..."); try { await sequelize.query(` DO $$ BEGIN -- Prüfe ob product_id NOT NULL Constraint existiert IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'falukant_data' AND table_name = 'transport' AND column_name = 'product_id' AND is_nullable = 'NO' ) THEN ALTER TABLE falukant_data.transport ALTER COLUMN product_id DROP NOT NULL; RAISE NOTICE 'product_id NOT NULL Constraint entfernt'; END IF; -- Prüfe ob size NOT NULL Constraint existiert IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'falukant_data' AND table_name = 'transport' AND column_name = 'size' AND is_nullable = 'NO' ) THEN ALTER TABLE falukant_data.transport ALTER COLUMN size DROP NOT NULL; RAISE NOTICE 'size NOT NULL Constraint entfernt'; END IF; END $$; `); console.log("✅ Transport product_id und size sind jetzt nullable"); } catch (e) { console.warn('⚠️ Konnte Transport-Spalten nicht nullable machen:', e?.message || e); } // Cleanup: Entferne verwaiste Einträge vor Schema-Updates console.log("Cleaning up orphaned entries..."); try { // Cleanup user_param_visibility const result1 = await sequelize.query(` DELETE FROM community.user_param_visibility WHERE param_id NOT IN ( SELECT id FROM community.user_param ); `); const deletedCount1 = result1[1] || 0; if (deletedCount1 > 0) { console.log(`✅ ${deletedCount1} verwaiste user_param_visibility Einträge entfernt`); } // Cleanup stock mit ungültigen branch_id (0 oder nicht existierend) const result2 = await sequelize.query(` DELETE FROM falukant_data.stock WHERE branch_id = 0 OR branch_id NOT IN ( SELECT id FROM falukant_data.branch ); `); const deletedCount2 = result2[1] || 0; if (deletedCount2 > 0) { console.log(`✅ ${deletedCount2} verwaiste stock Einträge entfernt`); } // Cleanup knowledge mit ungültigen character_id oder product_id const result3 = await sequelize.query(` DELETE FROM falukant_data.knowledge WHERE character_id NOT IN ( SELECT id FROM falukant_data.character ) OR product_id NOT IN ( SELECT id FROM falukant_type.product ); `); const deletedCount3 = result3[1] || 0; if (deletedCount3 > 0) { console.log(`✅ ${deletedCount3} verwaiste knowledge Einträge entfernt`); } if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0) { console.log("✅ Keine verwaisten Einträge gefunden"); } } catch (e) { console.warn('⚠️ Konnte verwaiste Einträge nicht bereinigen:', e?.message || e); } console.log("Setting up associations..."); setupAssociations(); console.log("Synchronizing models with schema updates..."); await syncModelsAlways(models); console.log("Initializing settings..."); await initializeSettings(); console.log("Initializing types..."); await initializeTypes(); console.log("Initializing user rights..."); await initializeUserRights(); console.log("Initializing image types..."); await initializeImageTypes(); console.log("Initializing forums..."); await initializeForum(); console.log("Initializing Falukant..."); await initializeFalukant(); console.log("Creating triggers..."); await createTriggers(); console.log("Initializing chat..."); await initializeChat(); // Match3-Initialisierung NACH der Model-Synchronisation UND nach der Erstellung aller Tabellen console.log("Initializing Match3..."); await initializeMatch3Data(); // Match3-Levels aktualisieren NACH der Initialisierung console.log("Updating existing Match3 levels..."); await updateExistingMatch3Levels(); console.log("Initializing Taxi..."); await initializeTaxi(); console.log('Database synchronization for deployment complete.'); } catch (error) { console.error('Unable to synchronize the database for deployment:', error); throw error; // Fehler weiterwerfen } }; export { syncDatabase, syncDatabaseForDeployment };