Refactor code and improve internationalization support
Some checks failed
Build & Push Docker Image / build (push) Has been cancelled
Some checks failed
Build & Push Docker Image / build (push) Has been cancelled
- Updated import statements to remove invisible characters. - Standardized comments to use a consistent hyphen format. - Adjusted username validation error messages for consistency. - Enhanced email sending functions to include language support. - Added email internationalization configuration for dynamic translations. - Updated calendar and federation routes to include language in user queries. - Improved user feedback messages in German and English for clarity.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Router } from 'express';
|
||||
import { Router } from 'express';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
@@ -37,7 +37,7 @@ const EMAIL_RE = /^[^\s@]{1,64}@[^\s@]{1,253}\.[^\s@]{2,}$/;
|
||||
// Simple format check for theme/language IDs (actual validation happens on the frontend)
|
||||
const SAFE_ID_RE = /^[a-zA-Z0-9_-]{1,50}$/;
|
||||
|
||||
// Allowlist for CSS color values – only permits hsl(), hex (#rgb/#rrggbb) and plain names
|
||||
// Allowlist for CSS color values - only permits hsl(), hex (#rgb/#rrggbb) and plain names
|
||||
const SAFE_COLOR_RE = /^(?:#[0-9a-fA-F]{3,8}|hsl\(\d{1,3},\s*\d{1,3}%,\s*\d{1,3}%\)|[a-zA-Z]{1,30})$/;
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 8;
|
||||
@@ -145,7 +145,7 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
|
||||
const usernameRegex = /^[a-zA-Z0-9_-]{3,30}$/;
|
||||
if (!usernameRegex.test(username)) {
|
||||
return res.status(400).json({ error: 'Username may only contain letters, numbers, _ and - (3–30 chars)' });
|
||||
return res.status(400).json({ error: 'Username may only contain letters, numbers, _ and - (3-30 chars)' });
|
||||
}
|
||||
|
||||
// M1: email format
|
||||
@@ -200,7 +200,7 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
await sendVerificationEmail(email.toLowerCase(), display_name, verifyUrl, appName);
|
||||
await sendVerificationEmail(email.toLowerCase(), display_name, verifyUrl, appName, 'en');
|
||||
} catch (mailErr) {
|
||||
log.auth.error(`Verification mail failed: ${mailErr.message}`);
|
||||
// Account is created but email failed — user can resend from login page
|
||||
@@ -210,7 +210,7 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
return res.status(201).json({ needsVerification: true, message: 'Verification email has been sent' });
|
||||
}
|
||||
|
||||
// No SMTP configured – register and login immediately (legacy behaviour)
|
||||
// No SMTP configured - register and login immediately (legacy behaviour)
|
||||
const result = await db.run(
|
||||
'INSERT INTO users (name, display_name, email, password_hash, email_verified) VALUES (?, ?, ?, ?, 1)',
|
||||
[username, display_name, email.toLowerCase(), hash]
|
||||
@@ -278,7 +278,7 @@ router.post('/resend-verification', resendVerificationLimiter, async (req, res)
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
const user = await db.get('SELECT id, name, display_name, email_verified, verification_resend_at FROM users WHERE email = ?', [email.toLowerCase()]);
|
||||
const user = await db.get('SELECT id, name, display_name, language, email_verified, verification_resend_at FROM users WHERE email = ?', [email.toLowerCase()]);
|
||||
|
||||
if (!user || user.email_verified) {
|
||||
// Don't reveal whether account exists
|
||||
@@ -313,7 +313,7 @@ router.post('/resend-verification', resendVerificationLimiter, async (req, res)
|
||||
}
|
||||
|
||||
try {
|
||||
await sendVerificationEmail(email.toLowerCase(), user.display_name || user.name, verifyUrl, appName);
|
||||
await sendVerificationEmail(email.toLowerCase(), user.display_name || user.name, verifyUrl, appName, user.language || 'en');
|
||||
} catch (mailErr) {
|
||||
log.auth.error(`Resend verification mail failed: ${mailErr.message}`);
|
||||
return res.status(502).json({ error: 'Email could not be sent. Please check your SMTP configuration.' });
|
||||
@@ -335,7 +335,7 @@ router.post('/login', loginLimiter, async (req, res) => {
|
||||
return res.status(400).json({ error: 'Email and password are required' });
|
||||
}
|
||||
|
||||
// M1: basic email format check – invalid format can never match a real account
|
||||
// M1: basic email format check - invalid format can never match a real account
|
||||
if (!EMAIL_RE.test(email)) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
@@ -361,7 +361,7 @@ router.post('/login', loginLimiter, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/auth/logout – revoke JWT via DragonflyDB blacklist
|
||||
// POST /api/auth/logout - revoke JWT via DragonflyDB blacklist
|
||||
router.post('/logout', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
@@ -432,7 +432,7 @@ router.put('/profile', authenticateToken, profileLimiter, async (req, res) => {
|
||||
if (name && name !== req.user.name) {
|
||||
const usernameRegex = /^[a-zA-Z0-9_-]{3,30}$/;
|
||||
if (!usernameRegex.test(name)) {
|
||||
return res.status(400).json({ error: 'Username may only contain letters, numbers, _ and - (3–30 chars)' });
|
||||
return res.status(400).json({ error: 'Username may only contain letters, numbers, _ and - (3-30 chars)' });
|
||||
}
|
||||
const existingUsername = await db.get('SELECT id FROM users WHERE LOWER(name) = LOWER(?) AND id != ?', [name, req.user.id]);
|
||||
if (existingUsername) {
|
||||
@@ -504,7 +504,7 @@ router.post('/avatar', authenticateToken, avatarLimiter, async (req, res) => {
|
||||
return res.status(400).json({ error: 'Only image files are allowed' });
|
||||
}
|
||||
|
||||
// M15: stream-level size limit – abort as soon as 2 MB is exceeded
|
||||
// M15: stream-level size limit - abort as soon as 2 MB is exceeded
|
||||
const MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
|
||||
Reference in New Issue
Block a user