Add mail verification and use .env insteads of environment in compose
Some checks failed
Build & Push Docker Image / build (push) Has been cancelled

This commit is contained in:
2026-02-24 20:35:08 +01:00
parent 3898bf1b4b
commit 8be973a166
14 changed files with 388 additions and 19 deletions

View File

@@ -134,6 +134,9 @@ export async function initDatabase() {
theme TEXT DEFAULT 'dark',
avatar_color TEXT DEFAULT '#6366f1',
avatar_image TEXT DEFAULT NULL,
email_verified INTEGER DEFAULT 0,
verification_token TEXT,
verification_token_expires TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
@@ -189,6 +192,9 @@ export async function initDatabase() {
theme TEXT DEFAULT 'dark',
avatar_color TEXT DEFAULT '#6366f1',
avatar_image TEXT DEFAULT NULL,
email_verified INTEGER DEFAULT 0,
verification_token TEXT,
verification_token_expires DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
@@ -247,6 +253,19 @@ export async function initDatabase() {
if (!(await db.columnExists('rooms', 'moderator_code'))) {
await db.exec('ALTER TABLE rooms ADD COLUMN moderator_code TEXT');
}
if (!(await db.columnExists('users', 'email_verified'))) {
await db.exec('ALTER TABLE users ADD COLUMN email_verified INTEGER DEFAULT 0');
}
if (!(await db.columnExists('users', 'verification_token'))) {
await db.exec('ALTER TABLE users ADD COLUMN verification_token TEXT');
}
if (!(await db.columnExists('users', 'verification_token_expires'))) {
if (isPostgres) {
await db.exec('ALTER TABLE users ADD COLUMN verification_token_expires TIMESTAMP');
} else {
await db.exec('ALTER TABLE users ADD COLUMN verification_token_expires DATETIME');
}
}
// ── Default admin ───────────────────────────────────────────────────────
const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
@@ -256,7 +275,7 @@ export async function initDatabase() {
if (!existingAdmin) {
const hash = bcrypt.hashSync(adminPassword, 12);
await db.run(
'INSERT INTO users (name, email, password_hash, role) VALUES (?, ?, ?, ?)',
'INSERT INTO users (name, email, password_hash, role, email_verified) VALUES (?, ?, ?, ?, 1)',
['Administrator', adminEmail, hash, 'admin']
);
console.log(`✅ Default admin created: ${adminEmail}`);

70
server/config/mailer.js Normal file
View File

@@ -0,0 +1,70 @@
import nodemailer from 'nodemailer';
let transporter;
export function initMailer() {
const host = process.env.SMTP_HOST;
const port = parseInt(process.env.SMTP_PORT || '587', 10);
const user = process.env.SMTP_USER;
const pass = process.env.SMTP_PASS;
if (!host || !user || !pass) {
console.warn('⚠️ SMTP not configured email verification disabled');
return false;
}
transporter = nodemailer.createTransport({
host,
port,
secure: port === 465,
auth: { user, pass },
});
console.log('✅ SMTP mailer configured');
return true;
}
export function isMailerConfigured() {
return !!transporter;
}
/**
* Send the verification email with a clickable link.
* @param {string} to recipient email
* @param {string} name user's display name
* @param {string} verifyUrl full verification URL
* @param {string} appName branding app name (default "Redlight")
*/
export async function sendVerificationEmail(to, name, verifyUrl, appName = 'Redlight') {
if (!transporter) {
throw new Error('SMTP not configured');
}
const from = process.env.SMTP_FROM || process.env.SMTP_USER;
await transporter.sendMail({
from: `"${appName}" <${from}>`,
to,
subject: `${appName} E-Mail bestätigen / Verify your email`,
html: `
<div style="font-family:Arial,sans-serif;max-width:520px;margin:0 auto;padding:32px;background:#1e1e2e;color:#cdd6f4;border-radius:12px;">
<h2 style="color:#cba6f7;margin-top:0;">Hey ${name} 👋</h2>
<p>Bitte bestätige deine E-Mail-Adresse, indem du auf den folgenden Button klickst:</p>
<p style="text-align:center;margin:28px 0;">
<a href="${verifyUrl}"
style="display:inline-block;background:#cba6f7;color:#1e1e2e;padding:12px 32px;border-radius:8px;text-decoration:none;font-weight:bold;">
E-Mail bestätigen
</a>
</p>
<p style="font-size:13px;color:#7f849c;">
Oder kopiere diesen Link in deinen Browser:<br/>
<a href="${verifyUrl}" style="color:#89b4fa;word-break:break-all;">${verifyUrl}</a>
</p>
<p style="font-size:13px;color:#7f849c;">Der Link ist 24 Stunden gültig.</p>
<hr style="border:none;border-top:1px solid #313244;margin:24px 0;"/>
<p style="font-size:12px;color:#585b70;">Falls du dich nicht registriert hast, ignoriere diese E-Mail.</p>
</div>
`,
text: `Hey ${name},\n\nBitte bestätige deine E-Mail: ${verifyUrl}\n\nDer Link ist 24 Stunden gültig.\n\n ${appName}`,
});
}