Init
This commit is contained in:
445
reset-db.js
Normal file
445
reset-db.js
Normal file
@@ -0,0 +1,445 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user