116 lines
4.2 KiB
JavaScript
116 lines
4.2 KiB
JavaScript
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 cron = require('node-cron');
|
||
|
||
app.use('/', indexRouter);
|
||
app.use('/admin', adminRouter);
|
||
app.use('/api', apiRouter);
|
||
app.use('/admin/billing', billingRouter);
|
||
|
||
// 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();
|
||
});
|