require('dotenv').config(); const express = require('express'); const session = require('express-session'); const path = require('path'); const bcrypt = require('bcryptjs'); const db = require('./config/database'); const app = express(); // View Engine app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // Static Files app.use(express.static(path.join(__dirname, 'public'))); // Body Parser app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Session app.use(session({ secret: process.env.SESSION_SECRET || 'plusfit24-secret', resave: false, saveUninitialized: false, cookie: { secure: false, // auf true setzen wenn HTTPS direkt (nicht via Proxy) maxAge: 24 * 60 * 60 * 1000 // 24 Stunden } })); // Routen const indexRouter = require('./routes/index'); const adminRouter = require('./routes/admin'); const apiRouter = require('./routes/api'); const billingRouter = require('./routes/billing'); const financeRouter = require('./routes/finance'); const cron = require('node-cron'); app.use('/', indexRouter); app.use('/admin', adminRouter); app.use('/api', apiRouter); app.use('/admin/billing', billingRouter); app.use('/admin/finance', financeRouter); // 404 Handler app.use((req, res) => { res.status(404).render('error', { message: 'Seite nicht gefunden' }); }); // Fehler Handler app.use((err, req, res, next) => { console.error(err.stack); res.status(500).render('error', { message: 'Ein Fehler ist aufgetreten' }); }); // Admin Account beim Start erstellen falls keiner existiert async function initAdmin() { try { const [rows] = await db.query('SELECT COUNT(*) as count FROM admins'); if (rows[0].count === 0) { const hash = await bcrypt.hash(process.env.ADMIN_PASSWORD || 'Admin1234!', 12); await db.query( 'INSERT INTO admins (username, password_hash) VALUES (?, ?)', [process.env.ADMIN_USER || 'admin', hash] ); console.log('✅ Admin Account erstellt:', process.env.ADMIN_USER || 'admin'); } } catch (err) { console.error('❌ Fehler beim Erstellen des Admin Accounts:', err.message); } } // Auto-Abrechnungslauf jeden 1. des Monats um 06:00 Uhr cron.schedule('0 6 1 * *', async () => { const { currentPeriod } = require('./routes/billing'); const period = currentPeriod(); console.log(`⏰ Auto-Abrechnungslauf gestartet für ${period}`); try { const [existing] = await db.query('SELECT COUNT(*) as c FROM invoices WHERE period = ?', [period]); if (existing[0].c > 0) { console.log(`⏭ Abrechnungslauf für ${period} bereits vorhanden – übersprungen`); return; } const [members] = await db.query(` SELECT m.*, t.price_monthly FROM memberships m JOIN tariffs t ON m.tariff_id = t.id WHERE m.status IN ('active','paused') AND m.contract_start <= LAST_DAY(STR_TO_DATE(CONCAT(?, '-01'), '%Y-%m-%d')) AND (m.contract_end IS NULL OR m.contract_end >= STR_TO_DATE(CONCAT(?, '-01'), '%Y-%m-%d')) `, [period, period]); const [runResult] = await db.query( 'INSERT INTO billing_runs (run_date, period, created_by) VALUES (CURDATE(), ?, ?)', [period, 'system-auto'] ); let total = 0, count = 0; for (const m of members) { const firstPeriod = m.first_payment_date ? m.first_payment_date.toISOString().substring(0,7) : null; const amount = firstPeriod === period && m.first_payment_amt ? parseFloat(m.first_payment_amt) : parseFloat(m.price_monthly); await db.query( 'INSERT IGNORE INTO invoices (billing_run_id, membership_id, period, amount, description, iban, account_holder, bank_name) VALUES (?,?,?,?,?,?,?,?)', [runResult.insertId, m.id, period, amount, `Mitgliedsbeitrag ${period}`, m.iban||'', m.account_holder||'', m.bank_name||''] ); total += amount; count++; } await db.query('UPDATE billing_runs SET total_amount=?, invoice_count=? WHERE id=?', [total, count, runResult.insertId]); console.log(`✅ Auto-Abrechnungslauf abgeschlossen: ${count} Rechnungen, ${total.toFixed(2)} €`); } catch (err) { console.error('❌ Auto-Abrechnungslauf Fehler:', err.message); } }); const PORT = process.env.PORT || 3100; app.listen(PORT, async () => { console.log(`🚀 PlusFit24 Server läuft auf Port ${PORT}`); await initAdmin(); });