const { formatBoolean, parseBoolean } = require('./helpers'); /** * Add restore_runs table for tracking restore operations */ exports.up = async function(knex) { // Create restore_runs table await knex.schema.createTable('restore_runs', table => { table.increments('id').primary(); // Timing table.timestamp('started_at').notNullable().defaultTo(knex.fn.now()); table.timestamp('completed_at'); table.integer('duration_seconds'); // Status and type table.string('status', 50).notNullable().defaultTo('running'); table.string('restore_type', 50).notNullable(); // full, database, files, selective // Source information table.string('source', 500).notNullable(); // Backup source path or S3 URL table.string('manifest_path', 500); // Path to manifest file // Results table.text('error_message'); table.text('statistics'); // JSON object with detailed statistics table.text('restore_log'); // JSON array of log entries // Safety backup table.string('pre_restore_backup_path', 500); // Path to pre-restore safety backup // Flags table.boolean('is_dry_run').defaultTo(formatBoolean(false)); table.boolean('was_rollback_attempted').defaultTo(formatBoolean(false)); table.boolean('was_successful').defaultTo(formatBoolean(false)); // Operator information table.string('operator_type', 50).defaultTo('manual'); // manual, scheduled, api table.integer('operator_user_id').references('id').inTable('admin_users').onDelete('SET NULL'); table.string('operator_ip', 50); // Metadata table.text('metadata'); // JSON object for additional data table.index(['status', 'started_at']); table.index(['restore_type', 'started_at']); }); // Create restore_file_operations table for tracking individual file operations await knex.schema.createTable('restore_file_operations', table => { table.increments('id').primary(); table.integer('restore_run_id').notNullable() .references('id').inTable('restore_runs').onDelete('CASCADE'); table.string('file_path', 500).notNullable(); table.string('operation', 50).notNullable(); // restore, skip, error table.string('status', 50).notNullable(); // pending, in_progress, completed, failed table.bigInteger('file_size'); table.string('checksum', 64); table.boolean('checksum_verified').defaultTo(formatBoolean(false)); table.text('error_message'); table.timestamp('started_at'); table.timestamp('completed_at'); table.index(['restore_run_id', 'status']); table.index(['file_path']); }); // Create restore_validation_results table await knex.schema.createTable('restore_validation_results', table => { table.increments('id').primary(); table.integer('restore_run_id').notNullable() .references('id').inTable('restore_runs').onDelete('CASCADE'); table.string('validation_type', 50).notNullable(); // pre-restore, post-restore table.boolean('is_valid').notNullable(); table.text('errors'); // JSON array of errors table.text('warnings'); // JSON array of warnings table.text('checksums'); // JSON object with checksum comparisons table.timestamp('validated_at').notNullable().defaultTo(knex.fn.now()); table.index(['restore_run_id', 'validation_type']); }); // Add restore-related settings to app_settings await knex('app_settings').insert([ { setting_key: 'restore_allow_force', setting_value: formatBoolean(false), setting_type: 'restore', description: 'Allow force restore with warnings', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { setting_key: 'restore_require_pre_backup', setting_value: formatBoolean(true), setting_type: 'restore', description: 'Require pre-restore backup', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { setting_key: 'restore_max_file_size_mb', setting_value: '5000', setting_type: 'restore', description: 'Maximum file size for restore (MB)', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { setting_key: 'restore_verify_checksums', setting_value: formatBoolean(true), setting_type: 'restore', description: 'Verify file checksums during restore', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { setting_key: 'restore_email_on_completion', setting_value: formatBoolean(true), setting_type: 'restore', description: 'Send email on restore completion', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { setting_key: 'restore_retention_days', setting_value: '30', setting_type: 'restore', description: 'Days to retain restore history', created_at: knex.fn.now(), updated_at: knex.fn.now() } ]); // Add new email templates for restore notifications const emailTemplates = [ { name: 'restore_completed', subject: '✅ Restore Completed Successfully', body: `

Restore Operation Completed

A restore operation has completed successfully.

Details:

Please verify that all systems are functioning correctly after the restore.

`, language: 'en', is_active: formatBoolean(true), created_at: knex.fn.now(), updated_at: knex.fn.now() }, { name: 'restore_failed', subject: '❌ Restore Operation Failed', body: `

Restore Operation Failed

A restore operation has failed and requires attention.

Details:

Please check the system logs for more details and take appropriate action.

Important: If a pre-restore backup was created, it may be used for recovery.

`, language: 'en', is_active: formatBoolean(true), created_at: knex.fn.now(), updated_at: knex.fn.now() }, { name: 'restore_completed', subject: '✅ Wiederherstellung erfolgreich abgeschlossen', body: `

Wiederherstellungsvorgang abgeschlossen

Ein Wiederherstellungsvorgang wurde erfolgreich abgeschlossen.

Details:

Bitte überprüfen Sie, ob alle Systeme nach der Wiederherstellung ordnungsgemäß funktionieren.

`, language: 'de', is_active: formatBoolean(true), created_at: knex.fn.now(), updated_at: knex.fn.now() }, { name: 'restore_failed', subject: '❌ Wiederherstellungsvorgang fehlgeschlagen', body: `

Wiederherstellungsvorgang fehlgeschlagen

Ein Wiederherstellungsvorgang ist fehlgeschlagen und erfordert Ihre Aufmerksamkeit.

Details:

Bitte überprüfen Sie die Systemprotokolle für weitere Details und ergreifen Sie entsprechende Maßnahmen.

Wichtig: Falls ein Backup vor der Wiederherstellung erstellt wurde, kann es zur Wiederherstellung verwendet werden.

`, language: 'de', is_active: formatBoolean(true), created_at: knex.fn.now(), updated_at: knex.fn.now() } ]; await knex('email_templates').insert(emailTemplates); }; exports.down = async function(knex) { // Remove email templates await knex('email_templates') .whereIn('name', ['restore_completed', 'restore_failed']) .delete(); // Remove settings await knex('app_settings') .where('setting_type', 'restore') .delete(); // Drop tables await knex.schema.dropTableIfExists('restore_validation_results'); await knex.schema.dropTableIfExists('restore_file_operations'); await knex.schema.dropTableIfExists('restore_runs'); };