diff --git a/backend/SQL_EXECUTION_ORDER.md b/backend/SQL_EXECUTION_ORDER.md new file mode 100644 index 0000000..97ab0d1 --- /dev/null +++ b/backend/SQL_EXECUTION_ORDER.md @@ -0,0 +1,178 @@ +# TimeClock v3 - SQL-Scripts Ausführungsreihenfolge + +Diese Anleitung zeigt, welche SQL-Scripts in welcher Reihenfolge ausgeführt werden müssen. + +## Auf dem Server ausführen: + +```bash +cd /var/www/timeclock/backend + +# DB-Credentials aus .env +DB_HOST=$(grep DB_HOST .env | cut -d= -f2) +DB_USER=$(grep DB_USER .env | cut -d= -f2) +DB_PASSWORD=$(grep DB_PASSWORD .env | cut -d= -f2) +DB_NAME=$(grep DB_NAME .env | cut -d= -f2) + +# Alias für einfachere Befehle +alias dbexec="mysql -h $DB_HOST -u $DB_USER -p'$DB_PASSWORD' $DB_NAME" +``` + +## 1. Basis-Schema (WICHTIG - zuerst!) + +```bash +# Erstellt alle Haupt-Tabellen +dbexec < database-schema.sql + +# Zeige erstellte Tabellen +dbexec -e "SHOW TABLES;" +``` + +## 2. Zusätzliche Tabellen + +```bash +# Invitation Table +dbexec < create-invitation-table.sql + +# Watcher Table +dbexec < create-watcher-table.sql +``` + +## 3. Indices für Performance + +```bash +# Sick Index +dbexec < add-sick-index.sql + +# Vacation Index +dbexec < add-vacation-index.sql +``` + +## 4. Timewish Setup (korrigierte Version!) + +```bash +# Timewish mit Zeiträumen +dbexec < setup-timewish-complete.sql + +# ODER falls noch Fehler: Nur die Spalten hinzufügen +dbexec << 'EOF' +-- Prüfe ob start_date existiert +SET @col_exists = (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'timewish' + AND COLUMN_NAME = 'start_date'); + +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE timewish ADD COLUMN start_date DATE DEFAULT ''2023-01-01'' AFTER end_time;', + 'SELECT ''start_date existiert bereits'';' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- end_date +SET @col_exists = (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'timewish' + AND COLUMN_NAME = 'end_date'); + +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE timewish ADD COLUMN end_date DATE DEFAULT NULL AFTER start_date;', + 'SELECT ''end_date existiert bereits'';' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +EOF +``` + +## 5. Optionale Scripts (nur bei Bedarf!) + +### User als Admin setzen +```bash +# Passe user_id an! (Deine User-ID aus der users-Tabelle) +dbexec < set-user-admin.sql +``` + +### Timezone-Fixes (nur falls Probleme mit Zeitzonen) +```bash +# Worklog Timezone korrigieren +dbexec < fix-worklog-timezone.sql +``` + +### Overtime Offset Update +```bash +dbexec < update-overtime-offset.sql +``` + +### Wednesday-Fix (nur falls benötigt) +```bash +dbexec < add-missing-wednesday.sql +``` + +## Überprüfung + +```bash +# Zeige alle Tabellen +dbexec -e "SHOW TABLES;" + +# Zeige timewish-Struktur +dbexec -e "DESCRIBE timewish;" + +# Zeige Anzahl User +dbexec -e "SELECT COUNT(*) AS user_count FROM users;" + +# Zeige deine Timewishes +dbexec -e "SELECT * FROM timewish WHERE user_id = 1 ORDER BY day, start_date;" +``` + +## Nach SQL-Ausführung: Backend neu starten + +```bash +# Backend neu starten damit es die neuen Tabellen/Spalten erkennt +pm2 restart timeclock-backend + +# Logs prüfen +pm2 logs timeclock-backend --lines 30 + +# Sollte zeigen: +# 🕐 TimeClock Server läuft auf Port 3010 +# 📍 API verfügbar unter http://localhost:3010/api +``` + +## Schnell-Befehl (alle wichtigen Scripts): + +```bash +cd /var/www/timeclock/backend + +# DB-Credentials setzen +DB_HOST=$(grep DB_HOST .env | cut -d= -f2) +DB_USER=$(grep DB_USER .env | cut -d= -f2) +DB_PASSWORD=$(grep DB_PASSWORD .env | cut -d= -f2) +DB_NAME=$(grep DB_NAME .env | cut -d= -f2) + +echo "Führe SQL-Scripts aus..." + +# 1. Basis-Schema +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < database-schema.sql 2>&1 | grep -v "already exists" || true + +# 2. Zusätzliche Tabellen +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < create-invitation-table.sql 2>&1 | grep -v "already exists" || true +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < create-watcher-table.sql 2>&1 | grep -v "already exists" || true + +# 3. Indices +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < add-sick-index.sql 2>&1 || true +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < add-vacation-index.sql 2>&1 || true + +# 4. Timewish (korrigiert) +mysql -h $DB_HOST -u $DB_USER -p"$DB_PASSWORD" $DB_NAME < setup-timewish-complete.sql + +echo "✅ SQL-Scripts ausgeführt!" + +# Backend neu starten +pm2 restart timeclock-backend + +echo "✅ Backend neu gestartet!" +``` + +Kopiere diesen Schnell-Befehl auf den Server und führe ihn aus! 🚀 + diff --git a/backend/reset-user-password.js b/backend/reset-user-password.js new file mode 100755 index 0000000..8fae63c --- /dev/null +++ b/backend/reset-user-password.js @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +// Reset User Password für TimeClock v3 +// Verwendung: node reset-user-password.js "email@example.com" "neues-passwort" + +require('dotenv').config(); +const bcrypt = require('bcrypt'); +const mysql = require('mysql2/promise'); + +async function resetPassword() { + const email = process.argv[2]; + const newPassword = process.argv[3]; + + if (!email || !newPassword) { + console.error('❌ Verwendung: node reset-user-password.js "email@example.com" "neues-passwort"'); + console.error(''); + console.error('Beispiel:'); + console.error(' node reset-user-password.js "tsschulz@gmx.net" "MeinNeuesPasswort123!"'); + process.exit(1); + } + + console.log('🔐 Setze neues Passwort für TimeClock v3...'); + console.log('Email:', email); + console.log(''); + + try { + // Datenbank-Verbindung + const connection = await mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME + }); + + console.log('✅ Datenbank verbunden'); + + // Neuen Hash erstellen (Node.js bcrypt, kompatibel mit der neuen App) + const saltRounds = 10; + const passwordHash = await bcrypt.hash(newPassword, saltRounds); + + console.log('✅ Passwort gehasht'); + console.log('Neuer Hash:', passwordHash); + console.log('Hash-Format:', passwordHash.substring(0, 4)); + console.log(''); + + // Update authinfo + const [result] = await connection.execute( + `UPDATE authinfo + SET password_hash = ?, + password_method = 'bcrypt', + email = ?, + status = 1, + failed_login_attempts = 0 + WHERE email = ? OR unverified_email = ?`, + [passwordHash, email, email, email] + ); + + if (result.affectedRows === 0) { + console.error('❌ Kein Benutzer mit dieser E-Mail gefunden!'); + console.log(''); + console.log('Verfügbare Benutzer:'); + const [users] = await connection.execute( + 'SELECT id, user_id, email, unverified_email FROM authinfo' + ); + console.table(users); + process.exit(1); + } + + console.log('✅ Passwort erfolgreich aktualisiert!'); + console.log('Betroffene Zeilen:', result.affectedRows); + console.log(''); + + // Zeige aktualisierte Daten + const [authInfo] = await connection.execute( + 'SELECT id, user_id, email, password_method, status, failed_login_attempts FROM authinfo WHERE email = ?', + [email] + ); + + console.log('📋 Aktualisierte AuthInfo:'); + console.table(authInfo); + + console.log(''); + console.log('🎉 Fertig! Du kannst dich jetzt mit folgenden Daten einloggen:'); + console.log(' Email:', email); + console.log(' Passwort:', newPassword); + + await connection.end(); + + } catch (error) { + console.error('❌ Fehler:', error.message); + process.exit(1); + } +} + +resetPassword(); + diff --git a/backend/src/services/AuthService.js b/backend/src/services/AuthService.js index 9568abf..828ddf8 100644 --- a/backend/src/services/AuthService.js +++ b/backend/src/services/AuthService.js @@ -1,6 +1,7 @@ const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const crypto = require('crypto'); +const nodemailer = require('nodemailer'); const database = require('../config/database'); /** @@ -12,6 +13,28 @@ class AuthService { this.jwtSecret = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; this.jwtExpiration = process.env.JWT_EXPIRATION || '24h'; this.saltRounds = 10; + this.emailTransporter = null; + this.initializeEmailTransporter(); + } + + /** + * Initialisiert den Email-Transporter + */ + initializeEmailTransporter() { + if (process.env.EMAIL_HOST && process.env.EMAIL_USER && process.env.EMAIL_PASSWORD) { + this.emailTransporter = nodemailer.createTransport({ + host: process.env.EMAIL_HOST, + port: parseInt(process.env.EMAIL_PORT) || 587, + secure: process.env.EMAIL_SECURE === 'true', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD + } + }); + console.log('✅ Email-Transporter konfiguriert'); + } else { + console.warn('⚠️ Email-Konfiguration fehlt - Passwort-Reset-E-Mails können nicht versendet werden'); + } } /** @@ -234,6 +257,71 @@ class AuthService { email_token_role: 1 // 1 = Password Reset }); + // E-Mail versenden (falls konfiguriert) + if (this.emailTransporter) { + const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5010'; + const resetUrl = `${frontendUrl}/password-reset?token=${resetToken}`; + + const mailOptions = { + from: `"${process.env.EMAIL_FROM_NAME || 'TimeClock'}" <${process.env.EMAIL_FROM}>`, + to: email, + subject: 'TimeClock - Passwort zurücksetzen', + text: `Hallo, + +du hast eine Passwort-Zurücksetzen-Anfrage für dein TimeClock-Konto gestellt. + +Klicke auf folgenden Link um dein Passwort zurückzusetzen: +${resetUrl} + +Dieser Link ist 1 Stunde gültig. + +Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail. + +--- +TimeClock Zeiterfassung +${frontendUrl} +`, + html: ` +
Hallo,
+du hast eine Passwort-Zurücksetzen-Anfrage für dein TimeClock-Konto gestellt.
+ +
+ Oder kopiere diesen Link in deinen Browser:
+ ${resetUrl}
+
+ Dieser Link ist 1 Stunde gültig. +
++ Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail. +
+
+ TimeClock Zeiterfassung
+ ${frontendUrl}
+
Dies ist eine Test-E-Mail von TimeClock v3.
+Wenn du diese E-Mail erhältst, funktioniert die E-Mail-Konfiguration korrekt!
+Gesendet am: ${new Date().toLocaleString('de-DE')}
+
+ TimeClock v3 Zeiterfassung
+ ${process.env.FRONTEND_URL || 'https://stechuhr3.tsschulz.de'}
+