// 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`); } // Cleanup notification mit ungültigen user_id const result4 = await sequelize.query(` DELETE FROM falukant_log.notification WHERE user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); `); const deletedCount4 = result4[1] || 0; if (deletedCount4 > 0) { console.log(`✅ ${deletedCount4} verwaiste notification Einträge entfernt`); } // Cleanup promotional_gift mit ungültigen sender_character_id oder recipient_character_id const result5 = await sequelize.query(` DELETE FROM falukant_log.promotional_gift WHERE sender_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR recipient_character_id NOT IN ( SELECT id FROM falukant_data.character ); `); const deletedCount5 = result5[1] || 0; if (deletedCount5 > 0) { console.log(`✅ ${deletedCount5} verwaiste promotional_gift Einträge entfernt`); } // Cleanup user_house mit ungültigen house_type_id oder user_id const result6 = await sequelize.query(` DELETE FROM falukant_data.user_house WHERE house_type_id NOT IN ( SELECT id FROM falukant_type.house ) OR user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); `); const deletedCount6 = result6[1] || 0; if (deletedCount6 > 0) { console.log(`✅ ${deletedCount6} verwaiste user_house Einträge entfernt`); } // Cleanup child_relation mit ungültigen father_character_id, mother_character_id oder child_character_id const result7 = await sequelize.query(` DELETE FROM falukant_data.child_relation WHERE father_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR mother_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR child_character_id NOT IN ( SELECT id FROM falukant_data.character ); `); const deletedCount7 = result7[1] || 0; if (deletedCount7 > 0) { console.log(`✅ ${deletedCount7} verwaiste child_relation Einträge entfernt`); } if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0 && deletedCount4 === 0 && deletedCount5 === 0 && deletedCount6 === 0 && deletedCount7 === 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 { // WICHTIG: Bei Caching-Problemen das Script neu starten // Node.js cached ES-Module, daher müssen Models neu geladen werden console.log('📦 Lade Models neu (Node.js Module-Cache wird verwendet)...'); // 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`); } // Cleanup notification mit ungültigen user_id const result4 = await sequelize.query(` DELETE FROM falukant_log.notification WHERE user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); `); const deletedCount4 = result4[1] || 0; if (deletedCount4 > 0) { console.log(`✅ ${deletedCount4} verwaiste notification Einträge entfernt`); } // Cleanup promotional_gift mit ungültigen sender_character_id oder recipient_character_id const result5 = await sequelize.query(` DELETE FROM falukant_log.promotional_gift WHERE sender_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR recipient_character_id NOT IN ( SELECT id FROM falukant_data.character ); `); const deletedCount5 = result5[1] || 0; if (deletedCount5 > 0) { console.log(`✅ ${deletedCount5} verwaiste promotional_gift Einträge entfernt`); } // Cleanup user_house mit ungültigen house_type_id oder user_id const result6 = await sequelize.query(` DELETE FROM falukant_data.user_house WHERE house_type_id NOT IN ( SELECT id FROM falukant_type.house ) OR user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); `); const deletedCount6 = result6[1] || 0; if (deletedCount6 > 0) { console.log(`✅ ${deletedCount6} verwaiste user_house Einträge entfernt`); } // Cleanup child_relation mit ungültigen father_character_id, mother_character_id oder child_character_id const result7 = await sequelize.query(` DELETE FROM falukant_data.child_relation WHERE father_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR mother_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR child_character_id NOT IN ( SELECT id FROM falukant_data.character ); `); const deletedCount7 = result7[1] || 0; if (deletedCount7 > 0) { console.log(`✅ ${deletedCount7} verwaiste child_relation Einträge entfernt`); } // Cleanup political_office mit ungültigen character_id, office_type_id oder region_id const result8 = await sequelize.query(` DELETE FROM falukant_data.political_office WHERE character_id NOT IN ( SELECT id FROM falukant_data.character ) OR office_type_id NOT IN ( SELECT id FROM falukant_type.political_office_type ) OR region_id NOT IN ( SELECT id FROM falukant_data.region ); `); const deletedCount8 = result8[1] || 0; if (deletedCount8 > 0) { console.log(`✅ ${deletedCount8} verwaiste political_office Einträge entfernt`); } if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0 && deletedCount4 === 0 && deletedCount5 === 0 && deletedCount6 === 0 && deletedCount7 === 0 && deletedCount8 === 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 };