104 lines
3.7 KiB
JavaScript
104 lines
3.7 KiB
JavaScript
const bcrypt = require('bcryptjs');
|
|
const { User } = require('../models');
|
|
const jwt = require('jsonwebtoken');
|
|
const { addTokenToBlacklist } = require('../utils/blacklist');
|
|
|
|
function delay(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
exports.register = async (req, res) => {
|
|
const { name, email, password } = req.body;
|
|
if (!name || !email || !password) {
|
|
return res.status(400).json({ message: 'Alle Felder sind erforderlich' });
|
|
}
|
|
try {
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
console.log('Register: creating user', { email });
|
|
|
|
const maxAttempts = 3;
|
|
let attempt = 0;
|
|
let createdUser = null;
|
|
let lastError = null;
|
|
|
|
while (attempt < maxAttempts && !createdUser) {
|
|
try {
|
|
createdUser = await User.create({ name, email, password: hashedPassword, active: true });
|
|
} catch (err) {
|
|
lastError = err;
|
|
// Spezifisch auf Lock-Timeout reagieren und erneut versuchen
|
|
if ((err.code === 'ER_LOCK_WAIT_TIMEOUT' || err?.parent?.code === 'ER_LOCK_WAIT_TIMEOUT') && attempt < maxAttempts - 1) {
|
|
const backoffMs = 300 * (attempt + 1);
|
|
console.warn(`Register: ER_LOCK_WAIT_TIMEOUT, retry in ${backoffMs}ms (attempt ${attempt + 1}/${maxAttempts})`);
|
|
await delay(backoffMs);
|
|
attempt++;
|
|
continue;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
if (!createdUser && lastError) {
|
|
console.error('Register error (after retries):', lastError);
|
|
return res.status(503).json({ message: 'Zeitüberschreitung beim Zugriff auf die Datenbank. Bitte erneut versuchen.' });
|
|
}
|
|
|
|
console.log('Register: user created', { id: createdUser.id });
|
|
|
|
const safeUser = {
|
|
id: createdUser.id,
|
|
name: createdUser.name,
|
|
email: createdUser.email,
|
|
active: createdUser.active,
|
|
created_at: createdUser.created_at
|
|
};
|
|
|
|
return res.status(201).json({ message: 'Benutzer erfolgreich registriert', user: safeUser });
|
|
} catch (error) {
|
|
if (error.name === 'SequelizeUniqueConstraintError') {
|
|
return res.status(400).json({ message: 'Email-Adresse bereits in Verwendung' });
|
|
}
|
|
console.error('Register error:', error);
|
|
return res.status(500).json({ message: 'Ein Fehler ist aufgetreten', error: error.message });
|
|
}
|
|
};
|
|
|
|
exports.login = async (req, res) => {
|
|
const { email, password } = req.body;
|
|
if (!email || !password) {
|
|
return res.status(400).json({ message: 'Email und Passwort sind erforderlich' });
|
|
}
|
|
try {
|
|
const user = await User.findOne({ where: { email } });
|
|
if (!user) {
|
|
return res.status(401).json({ message: 'Ungültige Anmeldedaten' });
|
|
}
|
|
const validPassword = await bcrypt.compare(password, user.password);
|
|
if (!validPassword) {
|
|
return res.status(401).json({ message: 'Ungültige Anmeldedaten' });
|
|
}
|
|
if (!user.active) {
|
|
return res.status(403).json({ message: 'Benutzerkonto ist nicht aktiv' });
|
|
}
|
|
const token = jwt.sign({ id: user.id, name: user.name, email: user.email }, 'zTxVgptmPl9!_dr%xxx9999(dd)', { expiresIn: '1h' });
|
|
return res.status(200).json({ message: 'Login erfolgreich', token, 'user': user });
|
|
} catch (error) {
|
|
return res.status(500).json({ message: 'Ein Fehler ist aufgetreten' });
|
|
}
|
|
};
|
|
|
|
exports.logout = async (req, res) => {
|
|
const authHeader = req.header('Authorization');
|
|
if (!authHeader) {
|
|
return res.status(400).json({ message: 'Kein Token bereitgestellt' });
|
|
}
|
|
const token = authHeader.replace('Bearer ', '');
|
|
try {
|
|
addTokenToBlacklist(token);
|
|
return res.status(200).json({ message: 'Logout erfolgreich' });
|
|
} catch (error) {
|
|
console.log(error);
|
|
return res.status(500).json({ message: 'Ein Fehler ist beim Logout aufgetreten' });
|
|
}
|
|
};
|