Update language, add LICENSE and README
All checks were successful
Build & Push Docker Image / build (push) Successful in 1m9s
All checks were successful
Build & Push Docker Image / build (push) Successful in 1m9s
This commit is contained in:
@@ -25,17 +25,17 @@ router.post('/register', async (req, res) => {
|
||||
const { name, email, password } = req.body;
|
||||
|
||||
if (!name || !email || !password) {
|
||||
return res.status(400).json({ error: 'Alle Felder sind erforderlich' });
|
||||
return res.status(400).json({ error: 'All fields are required' });
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
return res.status(400).json({ error: 'Passwort muss mindestens 6 Zeichen lang sein' });
|
||||
return res.status(400).json({ error: 'Password must be at least 6 characters long' });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
const existing = await db.get('SELECT id FROM users WHERE email = ?', [email]);
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'E-Mail wird bereits verwendet' });
|
||||
return res.status(409).json({ error: 'Email is already in use' });
|
||||
}
|
||||
|
||||
const hash = bcrypt.hashSync(password, 12);
|
||||
@@ -63,7 +63,7 @@ router.post('/register', async (req, res) => {
|
||||
|
||||
await sendVerificationEmail(email.toLowerCase(), name, verifyUrl, appName);
|
||||
|
||||
return res.status(201).json({ needsVerification: true, message: 'Verifizierungs-E-Mail wurde gesendet' });
|
||||
return res.status(201).json({ needsVerification: true, message: 'Verification email has been sent' });
|
||||
}
|
||||
|
||||
// No SMTP configured – register and login immediately (legacy behaviour)
|
||||
@@ -78,7 +78,7 @@ router.post('/register', async (req, res) => {
|
||||
res.status(201).json({ token, user });
|
||||
} catch (err) {
|
||||
console.error('Register error:', err);
|
||||
res.status(500).json({ error: 'Registrierung fehlgeschlagen' });
|
||||
res.status(500).json({ error: 'Registration failed' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ router.get('/verify-email', async (req, res) => {
|
||||
try {
|
||||
const { token } = req.query;
|
||||
if (!token) {
|
||||
return res.status(400).json({ error: 'Token fehlt' });
|
||||
return res.status(400).json({ error: 'Token is missing' });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
@@ -97,11 +97,11 @@ router.get('/verify-email', async (req, res) => {
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).json({ error: 'Ungültiger oder bereits verwendeter Token' });
|
||||
return res.status(400).json({ error: 'Invalid or already used token' });
|
||||
}
|
||||
|
||||
if (new Date(user.verification_token_expires) < new Date()) {
|
||||
return res.status(400).json({ error: 'Token ist abgelaufen. Bitte registriere dich erneut.' });
|
||||
return res.status(400).json({ error: 'Token has expired. Please register again.' });
|
||||
}
|
||||
|
||||
await db.run(
|
||||
@@ -109,10 +109,10 @@ router.get('/verify-email', async (req, res) => {
|
||||
[user.id]
|
||||
);
|
||||
|
||||
res.json({ verified: true, message: 'E-Mail erfolgreich verifiziert' });
|
||||
res.json({ verified: true, message: 'Email verified successfully' });
|
||||
} catch (err) {
|
||||
console.error('Verify email error:', err);
|
||||
res.status(500).json({ error: 'Verifizierung fehlgeschlagen' });
|
||||
res.status(500).json({ error: 'Verification failed' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -121,11 +121,11 @@ router.post('/resend-verification', async (req, res) => {
|
||||
try {
|
||||
const { email } = req.body;
|
||||
if (!email) {
|
||||
return res.status(400).json({ error: 'E-Mail ist erforderlich' });
|
||||
return res.status(400).json({ error: 'Email is required' });
|
||||
}
|
||||
|
||||
if (!isMailerConfigured()) {
|
||||
return res.status(400).json({ error: 'SMTP ist nicht konfiguriert' });
|
||||
return res.status(400).json({ error: 'SMTP is not configured' });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
@@ -133,7 +133,7 @@ router.post('/resend-verification', async (req, res) => {
|
||||
|
||||
if (!user || user.email_verified) {
|
||||
// Don't reveal whether account exists
|
||||
return res.json({ message: 'Falls ein Konto existiert, wurde eine neue E-Mail gesendet.' });
|
||||
return res.json({ message: 'If an account exists, a new email has been sent.' });
|
||||
}
|
||||
|
||||
const verificationToken = uuidv4();
|
||||
@@ -155,10 +155,10 @@ router.post('/resend-verification', async (req, res) => {
|
||||
|
||||
await sendVerificationEmail(email.toLowerCase(), user.name, verifyUrl, appName);
|
||||
|
||||
res.json({ message: 'Falls ein Konto existiert, wurde eine neue E-Mail gesendet.' });
|
||||
res.json({ message: 'If an account exists, a new email has been sent.' });
|
||||
} catch (err) {
|
||||
console.error('Resend verification error:', err);
|
||||
res.status(500).json({ error: 'E-Mail konnte nicht gesendet werden' });
|
||||
res.status(500).json({ error: 'Email could not be sent' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -168,18 +168,18 @@ router.post('/login', async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({ error: 'E-Mail und Passwort sind erforderlich' });
|
||||
return res.status(400).json({ error: 'Email and password are required' });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
const user = await db.get('SELECT * FROM users WHERE email = ?', [email.toLowerCase()]);
|
||||
|
||||
if (!user || !bcrypt.compareSync(password, user.password_hash)) {
|
||||
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
if (!user.email_verified && isMailerConfigured()) {
|
||||
return res.status(403).json({ error: 'E-Mail-Adresse noch nicht verifiziert. Bitte prüfe dein Postfach.', needsVerification: true });
|
||||
return res.status(403).json({ error: 'Email address not yet verified. Please check your inbox.', needsVerification: true });
|
||||
}
|
||||
|
||||
const token = generateToken(user.id);
|
||||
@@ -188,7 +188,7 @@ router.post('/login', async (req, res) => {
|
||||
res.json({ token, user: safeUser });
|
||||
} catch (err) {
|
||||
console.error('Login error:', err);
|
||||
res.status(500).json({ error: 'Anmeldung fehlgeschlagen' });
|
||||
res.status(500).json({ error: 'Login failed' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -206,7 +206,7 @@ router.put('/profile', authenticateToken, async (req, res) => {
|
||||
if (email && email !== req.user.email) {
|
||||
const existing = await db.get('SELECT id FROM users WHERE email = ? AND id != ?', [email.toLowerCase(), req.user.id]);
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'E-Mail wird bereits verwendet' });
|
||||
return res.status(409).json({ error: 'Email is already in use' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ router.put('/profile', authenticateToken, async (req, res) => {
|
||||
res.json({ user: updated });
|
||||
} catch (err) {
|
||||
console.error('Profile update error:', err);
|
||||
res.status(500).json({ error: 'Profil konnte nicht aktualisiert werden' });
|
||||
res.status(500).json({ error: 'Profile could not be updated' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -237,20 +237,20 @@ router.put('/password', authenticateToken, async (req, res) => {
|
||||
|
||||
const user = await db.get('SELECT password_hash FROM users WHERE id = ?', [req.user.id]);
|
||||
if (!bcrypt.compareSync(currentPassword, user.password_hash)) {
|
||||
return res.status(401).json({ error: 'Aktuelles Passwort ist falsch' });
|
||||
return res.status(401).json({ error: 'Current password is incorrect' });
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
return res.status(400).json({ error: 'Neues Passwort muss mindestens 6 Zeichen lang sein' });
|
||||
return res.status(400).json({ error: 'New password must be at least 6 characters long' });
|
||||
}
|
||||
|
||||
const hash = bcrypt.hashSync(newPassword, 12);
|
||||
await db.run('UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [hash, req.user.id]);
|
||||
|
||||
res.json({ message: 'Passwort erfolgreich geändert' });
|
||||
res.json({ message: 'Password changed successfully' });
|
||||
} catch (err) {
|
||||
console.error('Password change error:', err);
|
||||
res.status(500).json({ error: 'Passwort konnte nicht geändert werden' });
|
||||
res.status(500).json({ error: 'Password could not be changed' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -267,12 +267,12 @@ router.post('/avatar', authenticateToken, async (req, res) => {
|
||||
// Validate content type
|
||||
const contentType = req.headers['content-type'];
|
||||
if (!contentType || !contentType.startsWith('image/')) {
|
||||
return res.status(400).json({ error: 'Nur Bilddateien sind erlaubt' });
|
||||
return res.status(400).json({ error: 'Only image files are allowed' });
|
||||
}
|
||||
|
||||
// Max 2MB
|
||||
if (buffer.length > 2 * 1024 * 1024) {
|
||||
return res.status(400).json({ error: 'Bild darf maximal 2MB groß sein' });
|
||||
return res.status(400).json({ error: 'Image must not exceed 2MB' });
|
||||
}
|
||||
|
||||
const ext = contentType.includes('png') ? 'png' : contentType.includes('gif') ? 'gif' : contentType.includes('webp') ? 'webp' : 'jpg';
|
||||
@@ -295,7 +295,7 @@ router.post('/avatar', authenticateToken, async (req, res) => {
|
||||
res.json({ user: updated });
|
||||
} catch (err) {
|
||||
console.error('Avatar upload error:', err);
|
||||
res.status(500).json({ error: 'Avatar konnte nicht hochgeladen werden' });
|
||||
res.status(500).json({ error: 'Avatar could not be uploaded' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -313,7 +313,7 @@ router.delete('/avatar', authenticateToken, async (req, res) => {
|
||||
res.json({ user: updated });
|
||||
} catch (err) {
|
||||
console.error('Avatar delete error:', err);
|
||||
res.status(500).json({ error: 'Avatar konnte nicht entfernt werden' });
|
||||
res.status(500).json({ error: 'Avatar could not be removed' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -351,7 +351,7 @@ function generateColorFromName(name) {
|
||||
router.get('/avatar/:filename', (req, res) => {
|
||||
const filepath = path.join(uploadsDir, req.params.filename);
|
||||
if (!fs.existsSync(filepath)) {
|
||||
return res.status(404).json({ error: 'Avatar nicht gefunden' });
|
||||
return res.status(404).json({ error: 'Avatar not found' });
|
||||
}
|
||||
const ext = path.extname(filepath).slice(1);
|
||||
const mimeMap = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', webp: 'image/webp' };
|
||||
|
||||
Reference in New Issue
Block a user