const express = require('express'); const router = express.Router(); const crypto = require('crypto'); const db = require('../config/database'); const mailer = require('../config/mailer'); const { requireAdmin } = require('../middleware/auth'); // ============================================ // E-Mail Templates // ============================================ function renewalEmailHtml(member, tariffs, renewalLink, expiryDate) { const tarifList = tariffs.map(t => ` ${t.name} ${Number(t.price_monthly).toFixed(2).replace('.', ',')} €/Monat ${t.duration_months} Monate `).join(''); return `

Plusfit24

Deine Mitgliedschaft läuft bald ab

Hallo ${member.first_name} ${member.last_name},

deine Mitgliedschaft bei PlusFit24 läuft am ${new Date(expiryDate).toLocaleDateString('de-DE')} aus.

Wir würden uns freuen, dich weiterhin in unserem Studio begrüßen zu dürfen! Wähle jetzt deinen neuen Tarif:

${tarifList}
Tarif Preis Laufzeit
Jetzt Mitgliedschaft verlängern →

Dieser Link ist 30 Tage gültig. Falls du Fragen hast, melde dich gerne bei uns.
PlusFit24 UG · Moosleiten 12 · 84089 Aiglsbach

`; } // ============================================ // Öffentliche Verlängerungsseite (für Mitglieder) // ============================================ router.get('/renew/:token', async (req, res) => { try { const [rows] = await db.query(` SELECT r.*, m.first_name, m.last_name, m.email, m.agreed_price, m.tariff_id FROM renewal_requests r JOIN memberships m ON r.membership_id = m.id WHERE r.token = ? AND r.status = 'pending' AND r.expires_at > NOW() `, [req.params.token]); if (rows.length === 0) { return res.render('renewal-expired'); } const [tariffs] = await db.query( 'SELECT * FROM tariffs WHERE active = 1 ORDER BY price_monthly ASC' ); res.render('renewal', { request: rows[0], tariffs, error: null, success: null }); } catch (err) { console.error(err); res.render('error', { message: 'Fehler beim Laden der Seite.' }); } }); router.post('/renew/:token', async (req, res) => { const { tariff_id } = req.body; try { const [rows] = await db.query(` SELECT r.*, m.first_name, m.last_name, m.email, m.contract_end, m.effective_end FROM renewal_requests r JOIN memberships m ON r.membership_id = m.id WHERE r.token = ? AND r.status = 'pending' AND r.expires_at > NOW() `, [req.params.token]); if (rows.length === 0) return res.render('renewal-expired'); const request = rows[0]; const [tariffs] = await db.query('SELECT * FROM tariffs WHERE id = ? AND active = 1', [tariff_id]); if (tariffs.length === 0) { return res.render('renewal', { request, tariffs: [], error: 'Ungültiger Tarif.', success: null }); } const tariff = tariffs[0]; // Neues Vertragsende berechnen const startDate = new Date(request.effective_end || request.contract_end || new Date()); const newEnd = new Date(startDate); newEnd.setMonth(newEnd.getMonth() + tariff.duration_months); await db.query(` UPDATE memberships SET tariff_id = ?, agreed_price = ?, agreed_duration = ?, contract_end = ?, effective_end = ?, status = 'active' WHERE id = ? `, [ tariff.id, tariff.price_monthly, tariff.duration_months, newEnd, newEnd, request.membership_id ]); await db.query( "UPDATE renewal_requests SET status='completed', completed_at=NOW() WHERE id=?", [request.id] ); res.render('renewal', { request, tariffs: [tariff], error: null, success: `Deine Mitgliedschaft wurde erfolgreich verlängert! Neuer Tarif: ${tariff.name} bis ${newEnd.toLocaleDateString('de-DE')}` }); } catch (err) { console.error(err); res.render('error', { message: 'Fehler bei der Verlängerung.' }); } }); // ============================================ // Admin: Verlängerungs-E-Mail manuell senden // ============================================ router.post('/admin/send-renewal/:memberId', requireAdmin, async (req, res) => { const memberId = req.params.memberId; const backUrl = req.headers.referer || '/admin/finance'; try { const [members] = await db.query(` SELECT m.*, t.name as tariff_name FROM memberships m LEFT JOIN tariffs t ON m.tariff_id = t.id WHERE m.id = ? `, [memberId]); if (members.length === 0) return res.redirect(backUrl + '?error=Mitglied+nicht+gefunden'); const member = members[0]; // Alten Pending-Request invalidieren await db.query( "UPDATE renewal_requests SET status='expired' WHERE membership_id=? AND status='pending'", [memberId] ); // Neuen Token generieren const token = crypto.randomBytes(32).toString('hex'); const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 Tage await db.query( 'INSERT INTO renewal_requests (membership_id, token, expires_at) VALUES (?, ?, ?)', [memberId, token, expiresAt] ); // Aktive Tarife laden const [tariffs] = await db.query('SELECT * FROM tariffs WHERE active = 1 ORDER BY price_monthly ASC'); const baseUrl = `${req.protocol}://${req.get('host')}`; const renewalLink = `${baseUrl}/renew/${token}`; const expiryDate = member.effective_end || member.contract_end; // E-Mail senden await mailer.sendMail({ from: process.env.MAIL_FROM, to: member.email, subject: `Deine PlusFit24 Mitgliedschaft läuft am ${new Date(expiryDate).toLocaleDateString('de-DE')} ab`, html: renewalEmailHtml(member, tariffs, renewalLink, expiryDate) }); // Log await db.query( 'INSERT INTO email_log (membership_id, type, recipient, subject, status) VALUES (?,?,?,?,?)', [memberId, 'renewal', member.email, 'Mitgliedschaft läuft ab', 'sent'] ); res.redirect(backUrl + '?success=Verlängerungs-E-Mail+an+' + encodeURIComponent(member.email) + '+gesendet'); } catch (err) { console.error(err); // Log failure await db.query( 'INSERT INTO email_log (membership_id, type, recipient, subject, status) VALUES (?,?,?,?,?)', [memberId, 'renewal', '', 'Verlängerung', 'failed'] ).catch(() => {}); res.redirect(backUrl + '?error=E-Mail+Fehler:+' + encodeURIComponent(err.message)); } }); // Admin: Verlängerung manuell durchführen router.post('/admin/renew-manual/:memberId', requireAdmin, async (req, res) => { const { new_tariff_id } = req.body; const memberId = req.params.memberId; try { const [tariffs] = await db.query('SELECT * FROM tariffs WHERE id = ?', [new_tariff_id]); if (tariffs.length === 0) return res.redirect(`/admin/members/${memberId}?error=Tarif+nicht+gefunden`); const tariff = tariffs[0]; const [members] = await db.query('SELECT * FROM memberships WHERE id = ?', [memberId]); const member = members[0]; const startDate = new Date(member.effective_end || member.contract_end || new Date()); const newEnd = new Date(startDate); newEnd.setMonth(newEnd.getMonth() + tariff.duration_months); await db.query(` UPDATE memberships SET tariff_id = ?, agreed_price = ?, agreed_duration = ?, contract_end = ?, effective_end = ?, status = 'active' WHERE id = ? `, [tariff.id, tariff.price_monthly, tariff.duration_months, newEnd, newEnd, memberId]); res.redirect(`/admin/members/${memberId}?success=Vertrag+verlängert+bis+${newEnd.toLocaleDateString('de-DE')}`); } catch (err) { console.error(err); res.redirect(`/admin/members/${memberId}?error=Fehler+bei+Verlängerung`); } }); module.exports = router; module.exports.renewalEmailHtml = renewalEmailHtml;