Files
SDSStundenerfassung/reset-db.js
2026-01-30 10:44:07 +01:00

446 lines
15 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 'cn',
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);
});
});
}