const sqlite3 = require('sqlite3').verbose(); const bcrypt = require('bcryptjs'); const path = require('path'); const fs = require('fs'); const { exec } = require('child_process'); const { promisify } = require('util'); const execAsync = promisify(exec); const dbPath = path.join(__dirname, 'stundenerfassung.db'); console.log('🔄 Setze Datenbank zurück...\n'); // Datenbank schließen falls offen let db = null; let savedLdapConfig = []; // Hilfsfunktion zum Warten function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Funktion zum Prüfen und Beenden von Prozessen auf bestimmten Ports async function checkAndKillPorts(ports) { const killedProcesses = []; for (const port of ports) { try { // Prüfe, ob der Port belegt ist (Windows) const { stdout } = await execAsync(`netstat -ano | findstr :${port}`); if (stdout) { // Extrahiere PID aus der Ausgabe const lines = stdout.trim().split('\n'); const pids = new Set(); for (const line of lines) { const parts = line.trim().split(/\s+/); if (parts.length > 0) { const pid = parts[parts.length - 1]; if (pid && !isNaN(pid)) { pids.add(pid); } } } // Beende alle Prozesse, die den Port verwenden for (const pid of pids) { try { console.log(`🛑 Beende Prozess ${pid} auf Port ${port}...`); await execAsync(`taskkill /F /PID ${pid}`); killedProcesses.push({ port, pid }); console.log(`✅ Prozess ${pid} beendet`); } catch (err) { // Prozess könnte bereits beendet sein oder keine Berechtigung if (!err.message.includes('not found') && !err.message.includes('not running')) { console.log(`⚠️ Konnte Prozess ${pid} nicht beenden: ${err.message}`); } } } } } catch (err) { // Port ist nicht belegt oder netstat hat nichts gefunden if (!err.message.includes('findstr') && !err.message.includes('not found')) { // Ignoriere Fehler, wenn der Port nicht belegt ist } } } if (killedProcesses.length > 0) { console.log(`\n✅ ${killedProcesses.length} Prozess(e) beendet\n`); // Warte kurz, damit die Ports freigegeben werden await sleep(1000); } else { console.log('ℹ️ Keine Prozesse auf Ports 3333 oder 3334 gefunden\n'); } return killedProcesses.length > 0; } // Funktion zum Löschen der Datenbankdatei mit Retry-Logik (async) async function deleteDatabaseFile(retries = 10, initialDelay = 500) { if (!fs.existsSync(dbPath)) { return true; } for (let i = 0; i < retries; i++) { try { fs.unlinkSync(dbPath); console.log('✅ Datenbankdatei gelöscht\n'); return true; } catch (err) { if (err.code === 'EBUSY' && i < retries - 1) { // Exponentielle Backoff-Strategie const waitTime = initialDelay * Math.pow(2, i); console.log(`⏳ Datenbankdatei noch gesperrt (Versuch ${i + 1}/${retries}), warte ${waitTime}ms...`); await sleep(waitTime); continue; } if (i === retries - 1) { console.error(`❌ Konnte Datenbankdatei nach ${retries} Versuchen nicht löschen.`); console.error(' Bitte stellen Sie sicher, dass alle Prozesse geschlossen sind, die die Datenbank verwenden.'); return false; } throw err; } } return false; } // Promise-Wrapper für sqlite3 Database.all function dbAll(db, query, params = []) { return new Promise((resolve, reject) => { db.all(query, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } // Promise-Wrapper für sqlite3 Database.close function dbClose(db) { return new Promise((resolve, reject) => { db.close((err) => { if (err) reject(err); else resolve(); }); }); } // Promise-Wrapper für sqlite3 Database-Konstruktor function openDatabase(path, mode = sqlite3.OPEN_READONLY) { return new Promise((resolve, reject) => { const db = new sqlite3.Database(path, mode, (err) => { if (err) reject(err); else resolve(db); }); }); } // Hauptfunktion als async async function resetDatabase() { try { // Prüfe und beende Prozesse auf Ports 3333 und 3334 console.log('🔍 Prüfe auf laufende Server auf Ports 3333 und 3334...\n'); await checkAndKillPorts([3333, 3334]); // Prüfe ob Datenbank existiert und sichere ldap_config Daten if (fs.existsSync(dbPath)) { console.log('📁 Datenbankdatei gefunden...'); try { // Temporäre Datenbankverbindung zum Lesen der ldap_config const tempDb = await openDatabase(dbPath, sqlite3.OPEN_READONLY); try { // Lese ldap_config Daten const rows = await dbAll(tempDb, 'SELECT * FROM ldap_config'); if (rows && rows.length > 0) { savedLdapConfig = rows; console.log(`💾 ${rows.length} LDAP-Konfiguration(en) gesichert\n`); } else { console.log('ℹ️ Keine LDAP-Konfiguration vorhanden\n'); } } catch (err) { console.log('ℹ️ ldap_config Tabelle existiert nicht oder konnte nicht gelesen werden\n'); } // Schließe die temporäre Datenbank await dbClose(tempDb); // Warte etwas länger, damit die Datenbank wirklich geschlossen ist await sleep(500); // Datenbank löschen const success = await deleteDatabaseFile(); if (success) { createNewDatabase(); } else { console.error('❌ Konnte Datenbankdatei nicht löschen'); process.exit(1); } } catch (err) { console.log('⚠️ Konnte Datenbank nicht öffnen zum Lesen, fahre fort...\n'); // Datenbank löschen const success = await deleteDatabaseFile(); if (success) { createNewDatabase(); } else { console.error('❌ Konnte Datenbankdatei nicht löschen'); process.exit(1); } } } else { console.log('ℹ️ Datenbankdatei existiert nicht, erstelle neue...\n'); createNewDatabase(); } } catch (error) { console.error('❌ Fehler beim Zurücksetzen der Datenbank:', error); if (db) { db.close(); } process.exit(1); } } // Starte das Reset resetDatabase().catch((error) => { console.error('❌ Unerwarteter Fehler:', error); process.exit(1); }); function createNewDatabase() { // Neue Datenbank erstellen db = new sqlite3.Database(dbPath); db.serialize(() => { console.log('📊 Erstelle Tabellen...\n'); // Benutzer-Tabelle db.run(`CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, firstname TEXT NOT NULL, lastname TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'mitarbeiter', last_week_start TEXT, personalnummer TEXT, wochenstunden REAL, urlaubstage REAL, overtime_offset_hours REAL DEFAULT 0, ping_ip TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, (err) => { if (err) console.error('Fehler bei users:', err); else console.log('✅ Tabelle users erstellt'); }); // Stundenerfassung-Tabelle db.run(`CREATE TABLE timesheet_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, date TEXT NOT NULL, start_time TEXT, end_time TEXT, break_minutes INTEGER DEFAULT 0, total_hours REAL, notes TEXT, activity1_desc TEXT, activity1_hours REAL, activity1_project_number TEXT, activity2_desc TEXT, activity2_hours REAL, activity2_project_number TEXT, activity3_desc TEXT, activity3_hours REAL, activity3_project_number TEXT, activity4_desc TEXT, activity4_hours REAL, activity4_project_number TEXT, activity5_desc TEXT, activity5_hours REAL, activity5_project_number TEXT, overtime_taken_hours REAL, vacation_type TEXT, sick_status INTEGER DEFAULT 0, pause_start_time TEXT, pause_end_time TEXT, status TEXT DEFAULT 'offen', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) )`, (err) => { if (err) console.error('Fehler bei timesheet_entries:', err); else console.log('✅ Tabelle timesheet_entries erstellt'); }); // Wöchentliche Stundenzettel-Tabelle db.run(`CREATE TABLE weekly_timesheets ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, week_start TEXT NOT NULL, week_end TEXT NOT NULL, version INTEGER DEFAULT 1, status TEXT DEFAULT 'eingereicht', submitted_at DATETIME DEFAULT CURRENT_TIMESTAMP, reviewed_by INTEGER, reviewed_at DATETIME, pdf_downloaded_at DATETIME, pdf_downloaded_by INTEGER, version_reason TEXT, admin_comment TEXT, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (reviewed_by) REFERENCES users(id), FOREIGN KEY (pdf_downloaded_by) REFERENCES users(id) )`, (err) => { if (err) console.error('Fehler bei weekly_timesheets:', err); else console.log('✅ Tabelle weekly_timesheets erstellt'); }); // LDAP-Konfiguration-Tabelle db.run(`CREATE TABLE ldap_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, enabled INTEGER DEFAULT 0, url TEXT, bind_dn TEXT, bind_password TEXT, base_dn TEXT, user_search_filter TEXT, username_attribute TEXT DEFAULT 'sAMAccountName', firstname_attribute TEXT DEFAULT 'givenName', lastname_attribute TEXT DEFAULT 'sn', sync_interval INTEGER DEFAULT 0, last_sync DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, (err) => { if (err) console.error('Fehler bei ldap_config:', err); else { console.log('✅ Tabelle ldap_config erstellt'); // Stelle gesicherte LDAP-Konfiguration wieder her if (savedLdapConfig.length > 0) { console.log('🔄 Stelle LDAP-Konfiguration wieder her...'); savedLdapConfig.forEach((config) => { db.run(`INSERT INTO ldap_config ( id, enabled, url, bind_dn, bind_password, base_dn, user_search_filter, username_attribute, firstname_attribute, lastname_attribute, sync_interval, last_sync, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ config.id, config.enabled, config.url, config.bind_dn, config.bind_password, config.base_dn, config.user_search_filter, config.username_attribute, config.firstname_attribute, config.lastname_attribute, config.sync_interval, config.last_sync, config.created_at, config.updated_at ], (err) => { if (err) { console.error('Fehler beim Wiederherstellen der LDAP-Konfiguration:', err); } else { console.log(`✅ LDAP-Konfiguration (ID: ${config.id}) wiederhergestellt`); } }); }); } } }); // LDAP-Sync-Log-Tabelle db.run(`CREATE TABLE ldap_sync_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_type TEXT NOT NULL, status TEXT NOT NULL, users_synced INTEGER DEFAULT 0, error_message TEXT, sync_started_at DATETIME DEFAULT CURRENT_TIMESTAMP, sync_completed_at DATETIME )`, (err) => { if (err) console.error('Fehler bei ldap_sync_log:', err); else console.log('✅ Tabelle ldap_sync_log erstellt'); }); // Ping-Status-Tabelle für IP-basierte Zeiterfassung db.run(`CREATE TABLE ping_status ( user_id INTEGER NOT NULL, date TEXT NOT NULL, last_successful_ping DATETIME, failed_ping_count INTEGER DEFAULT 0, start_time_set INTEGER DEFAULT 0, first_failed_ping_time DATETIME, PRIMARY KEY (user_id, date), FOREIGN KEY (user_id) REFERENCES users(id) )`, (err) => { if (err) console.error('Fehler bei ping_status:', err); else console.log('✅ Tabelle ping_status erstellt'); }); // Warte bis alle Tabellen erstellt sind db.run('SELECT 1', (err) => { if (err) { console.error('Fehler beim Warten:', err); return; } console.log('\n👤 Erstelle Standard-Benutzer...\n'); // Standard Admin-Benutzer (Rolle als JSON-Array) const adminPassword = bcrypt.hashSync('admin123', 10); db.run(`INSERT INTO users (id, username, password, firstname, lastname, role) VALUES (1, 'admin', ?, 'System', 'Administrator', ?)`, [adminPassword, JSON.stringify(['admin'])], (err) => { if (err) console.error('Fehler beim Erstellen des Admin-Users:', err); else console.log('✅ Admin-User erstellt (admin / admin123)'); }); // Standard Verwaltungs-Benutzer (Rolle als JSON-Array) const verwaltungPassword = bcrypt.hashSync('verwaltung123', 10); db.run(`INSERT INTO users (id, username, password, firstname, lastname, role) VALUES (2, 'verwaltung', ?, 'Verwaltung', 'User', ?)`, [verwaltungPassword, JSON.stringify(['verwaltung'])], (err) => { if (err) console.error('Fehler beim Erstellen des Verwaltungs-Users:', err); else console.log('✅ Verwaltungs-User erstellt (verwaltung / verwaltung123)'); }); // Test-Mitarbeiter (optional, Rolle als JSON-Array) const mitarbeiterPassword = bcrypt.hashSync('test123', 10); db.run(`INSERT INTO users (id, username, password, firstname, lastname, role, wochenstunden, urlaubstage) VALUES (3, 'test', ?, 'Test', 'Mitarbeiter', ?, 40, 25)`, [mitarbeiterPassword, JSON.stringify(['mitarbeiter'])], (err) => { if (err && !err.message.includes('UNIQUE constraint')) { console.error('Fehler beim Erstellen des Test-Users:', err); } else if (!err) { console.log('✅ Test-Mitarbeiter erstellt (test / test123, 40h/Woche, 25 Urlaubstage)'); } }); // Warte bis alle Benutzer erstellt sind setTimeout(() => { console.log('\n✨ Datenbank erfolgreich zurückgesetzt!\n'); console.log('📋 Standard-Zugangsdaten:'); console.log(' Admin: admin / admin123'); console.log(' Verwaltung: verwaltung / verwaltung123'); console.log(' Test-User: test / test123\n'); db.close((err) => { if (err) { console.error('Fehler beim Schließen der Datenbank:', err); process.exit(1); } else { console.log('✅ Datenbank geschlossen\n'); process.exit(0); } }); }, 500); }); }); }