Add email functionality to AuthService; implement password reset email feature with nodemailer, including transporter initialization and email template for user notifications.
This commit is contained in:
178
backend/SQL_EXECUTION_ORDER.md
Normal file
178
backend/SQL_EXECUTION_ORDER.md
Normal file
@@ -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! 🚀
|
||||||
|
|
||||||
96
backend/reset-user-password.js
Executable file
96
backend/reset-user-password.js
Executable file
@@ -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();
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
const database = require('../config/database');
|
const database = require('../config/database');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +13,28 @@ class AuthService {
|
|||||||
this.jwtSecret = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
this.jwtSecret = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||||
this.jwtExpiration = process.env.JWT_EXPIRATION || '24h';
|
this.jwtExpiration = process.env.JWT_EXPIRATION || '24h';
|
||||||
this.saltRounds = 10;
|
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
|
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: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2 style="color: #333;">Passwort zurücksetzen</h2>
|
||||||
|
<p>Hallo,</p>
|
||||||
|
<p>du hast eine Passwort-Zurücksetzen-Anfrage für dein TimeClock-Konto gestellt.</p>
|
||||||
|
<p>
|
||||||
|
<a href="${resetUrl}" style="display: inline-block; padding: 12px 24px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 16px 0;">
|
||||||
|
Passwort zurücksetzen
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
Oder kopiere diesen Link in deinen Browser:<br>
|
||||||
|
<code style="background: #f5f5f5; padding: 4px 8px; border-radius: 4px; display: inline-block; margin-top: 8px;">${resetUrl}</code>
|
||||||
|
</p>
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
<strong>Dieser Link ist 1 Stunde gültig.</strong>
|
||||||
|
</p>
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail.
|
||||||
|
</p>
|
||||||
|
<hr style="border: none; border-top: 1px solid #ddd; margin: 24px 0;">
|
||||||
|
<p style="color: #999; font-size: 12px;">
|
||||||
|
TimeClock Zeiterfassung<br>
|
||||||
|
<a href="${frontendUrl}">${frontendUrl}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.emailTransporter.sendMail(mailOptions);
|
||||||
|
console.log('✅ Passwort-Reset E-Mail gesendet an:', email);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim E-Mail-Versand:', error.message);
|
||||||
|
// Werfe keinen Fehler - Token wurde gespeichert, User kann es anders nutzen
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ E-Mail-Transporter nicht konfiguriert - Reset-Token erstellt aber keine E-Mail versendet');
|
||||||
|
console.log('Reset-Token:', resetToken);
|
||||||
|
}
|
||||||
|
|
||||||
return resetToken;
|
return resetToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
115
backend/test-email.js
Executable file
115
backend/test-email.js
Executable file
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Test E-Mail-Konfiguration
|
||||||
|
// Verwendung: node test-email.js "empfaenger@example.com"
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
|
const recipientEmail = process.argv[2] || 'tsschulz@gmx.net';
|
||||||
|
|
||||||
|
console.log('📧 Teste E-Mail-Konfiguration...');
|
||||||
|
console.log('');
|
||||||
|
console.log('SMTP-Server:', process.env.EMAIL_HOST);
|
||||||
|
console.log('Port:', process.env.EMAIL_PORT);
|
||||||
|
console.log('Secure:', process.env.EMAIL_SECURE);
|
||||||
|
console.log('User:', process.env.EMAIL_USER);
|
||||||
|
console.log('From:', process.env.EMAIL_FROM);
|
||||||
|
console.log('An:', recipientEmail);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// Erstelle Transporter
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.EMAIL_HOST,
|
||||||
|
port: parseInt(process.env.EMAIL_PORT),
|
||||||
|
secure: process.env.EMAIL_SECURE === 'true',
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL_USER,
|
||||||
|
pass: process.env.EMAIL_PASSWORD
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test-E-Mail senden
|
||||||
|
async function sendTestEmail() {
|
||||||
|
try {
|
||||||
|
console.log('🔄 Verbinde mit SMTP-Server...');
|
||||||
|
|
||||||
|
// Verify connection
|
||||||
|
await transporter.verify();
|
||||||
|
console.log('✅ SMTP-Verbindung erfolgreich!');
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
console.log('📤 Sende Test-E-Mail...');
|
||||||
|
|
||||||
|
const info = await transporter.sendMail({
|
||||||
|
from: `"${process.env.EMAIL_FROM_NAME || 'TimeClock'}" <${process.env.EMAIL_FROM}>`,
|
||||||
|
to: recipientEmail,
|
||||||
|
subject: 'TimeClock v3 - Test E-Mail',
|
||||||
|
text: `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'}
|
||||||
|
`,
|
||||||
|
html: `
|
||||||
|
<h2>TimeClock v3 - Test E-Mail</h2>
|
||||||
|
<p>Dies ist eine Test-E-Mail von TimeClock v3.</p>
|
||||||
|
<p><strong>Wenn du diese E-Mail erhältst, funktioniert die E-Mail-Konfiguration korrekt!</strong></p>
|
||||||
|
<p>Gesendet am: ${new Date().toLocaleString('de-DE')}</p>
|
||||||
|
<hr>
|
||||||
|
<p style="color: #666; font-size: 12px;">
|
||||||
|
TimeClock v3 Zeiterfassung<br>
|
||||||
|
<a href="${process.env.FRONTEND_URL || 'https://stechuhr3.tsschulz.de'}">${process.env.FRONTEND_URL || 'https://stechuhr3.tsschulz.de'}</a>
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
console.log('✅ E-Mail erfolgreich gesendet!');
|
||||||
|
console.log('');
|
||||||
|
console.log('Message ID:', info.messageId);
|
||||||
|
console.log('Response:', info.response);
|
||||||
|
console.log('');
|
||||||
|
console.log('🎉 E-Mail-Konfiguration funktioniert!');
|
||||||
|
console.log('');
|
||||||
|
console.log('📬 Prüfe dein E-Mail-Postfach:', recipientEmail);
|
||||||
|
console.log(' (inkl. Spam-Ordner)');
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('');
|
||||||
|
console.error('❌ Fehler beim E-Mail-Versand:');
|
||||||
|
console.error('');
|
||||||
|
console.error(error.message);
|
||||||
|
console.error('');
|
||||||
|
|
||||||
|
if (error.code === 'EAUTH') {
|
||||||
|
console.error('💡 SMTP-Authentifizierung fehlgeschlagen!');
|
||||||
|
console.error(' Prüfe EMAIL_USER und EMAIL_PASSWORD in .env');
|
||||||
|
} else if (error.code === 'ECONNECTION') {
|
||||||
|
console.error('💡 Verbindung zum SMTP-Server fehlgeschlagen!');
|
||||||
|
console.error(' Prüfe EMAIL_HOST und EMAIL_PORT in .env');
|
||||||
|
} else if (error.code === 'ETIMEDOUT') {
|
||||||
|
console.error('💡 Timeout beim Verbinden!');
|
||||||
|
console.error(' Prüfe Firewall-Einstellungen (Port', process.env.EMAIL_PORT, ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('');
|
||||||
|
console.error('Aktuelle Konfiguration:');
|
||||||
|
console.error(' EMAIL_HOST:', process.env.EMAIL_HOST);
|
||||||
|
console.error(' EMAIL_PORT:', process.env.EMAIL_PORT);
|
||||||
|
console.error(' EMAIL_SECURE:', process.env.EMAIL_SECURE);
|
||||||
|
console.error(' EMAIL_USER:', process.env.EMAIL_USER);
|
||||||
|
console.error(' EMAIL_FROM:', process.env.EMAIL_FROM);
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTestEmail();
|
||||||
|
|
||||||
45
backend/test-password.js
Normal file
45
backend/test-password.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Test-Script für Passwort-Verifizierung
|
||||||
|
// Verwendung: node test-password.js "dein-passwort"
|
||||||
|
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
const hash = '$2y$07$cSnPaBjKKlmtOTKtbTDTTuYQ0bZ0upNQfNWf7gf8OKiz8eEjwSGFG';
|
||||||
|
const password = process.argv[2];
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
console.error('❌ Verwendung: node test-password.js "dein-passwort"');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🔐 Teste Passwort-Verifizierung...');
|
||||||
|
console.log('Hash:', hash);
|
||||||
|
console.log('Hash-Format:', hash.substring(0, 4));
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// bcrypt.compare kann $2y$ Hashes lesen (PHP-Format)
|
||||||
|
bcrypt.compare(password, hash)
|
||||||
|
.then(isMatch => {
|
||||||
|
if (isMatch) {
|
||||||
|
console.log('✅ Passwort ist KORREKT!');
|
||||||
|
console.log('');
|
||||||
|
console.log('Das Passwort-Hashing funktioniert.');
|
||||||
|
console.log('Problem liegt woanders (z.B. Salt, Email-Matching, etc.)');
|
||||||
|
} else {
|
||||||
|
console.log('❌ Passwort ist FALSCH!');
|
||||||
|
console.log('');
|
||||||
|
console.log('Entweder:');
|
||||||
|
console.log('1. Falsches Passwort eingegeben');
|
||||||
|
console.log('2. Hash wurde anders erstellt (Salt-Problem)');
|
||||||
|
console.log('3. Passwort muss neu gesetzt werden');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('❌ Fehler beim Vergleich:', err.message);
|
||||||
|
console.log('');
|
||||||
|
console.log('Mögliche Ursachen:');
|
||||||
|
console.log('- $2y$ Format wird nicht unterstützt');
|
||||||
|
console.log('- Hash ist korrupt');
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user