diff --git a/public/css/style.css b/public/css/style.css index 1654370..fa8c95a 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1343,3 +1343,36 @@ body:not(.admin-body) > * { } .invoice-history-row:last-child td { border-bottom: none; } .invoice-history-row:hover td { background: #f8f8ff; cursor: default; } + +/* ---- NFC / Zugangskarte ---- */ +.token-wrap { + display: flex; + align-items: center; + gap: 10px; + background: #f1f0ff; + border: 1.5px solid #c4b5fd; + border-radius: 8px; + padding: 10px 14px; +} +.token-display { + font-family: monospace; + font-size: 0.88rem; + letter-spacing: 1px; + color: #4c1d95; + flex: 1; + word-break: break-all; +} +.token-regen-btn { flex-shrink: 0; } + +.nfc-info-box { + margin-top: 14px; + background: #f0fdf4; + border: 1.5px solid #bbf7d0; + border-radius: 8px; + padding: 12px 16px; + font-size: 0.82rem; + color: #166534; + line-height: 1.6; +} +.nfc-info-box strong { display: block; margin-bottom: 6px; } +.nfc-info-box ol { padding-left: 18px; } diff --git a/routes/admin.js b/routes/admin.js index a988f93..913e67a 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -1,4 +1,5 @@ const express = require('express'); +const crypto = require('crypto'); const router = express.Router(); const bcrypt = require('bcryptjs'); const db = require('../config/database'); @@ -289,4 +290,32 @@ router.post('/members/:id/pauses/:pauseId/delete', requireAdmin, async (req, res } }); + +// ===== NFC / ZUGANGSKARTE ===== +router.post('/members/:id/regenerate-token', requireAdmin, async (req, res) => { + try { + const token = crypto.randomBytes(16).toString('hex'); + await db.query( + 'UPDATE memberships SET access_token = ? WHERE id = ?', + [token, req.params.id] + ); + res.redirect(`/admin/members/${req.params.id}?success=Neuer+Token+generiert`); + } catch (err) { + res.redirect(`/admin/members/${req.params.id}?error=Fehler+beim+Generieren`); + } +}); + +router.post('/members/:id/update-nfc', requireAdmin, async (req, res) => { + const { nfc_uid } = req.body; + try { + await db.query( + 'UPDATE memberships SET nfc_uid = ?, card_issued = 1, card_issued_at = NOW() WHERE id = ?', + [nfc_uid ? nfc_uid.trim().toUpperCase() : null, req.params.id] + ); + res.redirect(`/admin/members/${req.params.id}?success=NFC+UID+gespeichert`); + } catch (err) { + res.redirect(`/admin/members/${req.params.id}?error=Fehler+beim+Speichern`); + } +}); + module.exports = router; diff --git a/routes/api.js b/routes/api.js index 6eb7c2e..3d8a355 100644 --- a/routes/api.js +++ b/routes/api.js @@ -1,4 +1,5 @@ const express = require('express'); +const crypto = require('crypto'); const router = express.Router(); const dns = require('dns').promises; const db = require('../config/database'); @@ -118,20 +119,23 @@ router.post('/submit-membership', async (req, res) => { return res.json({ success: false, error: 'Ungültiger oder inaktiver Tarif.' }); } + // Zugangstoken generieren + const access_token = crypto.randomBytes(16).toString('hex'); + // In DB speichern await db.query(` INSERT INTO memberships (tariff_id, salutation, title, first_name, last_name, birth_date, email, phone, street, address_addition, zip, city, bank_name, account_holder, iban, - sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent, is_minor) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent, is_minor, access_token) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ tariff_id, salutation, title || '', first_name, last_name, birth_date, email, phone || '', street, address_addition || '', zip, city, bank_name || '', account_holder || '', iban || '', sepa_accepted ? 1 : 0, agb_accepted ? 1 : 0, datenschutz_accepted ? 1 : 0, data_correct ? 1 : 0, - guardian_consent ? 1 : 0, is_minor + guardian_consent ? 1 : 0, is_minor, access_token ]); res.json({ success: true }); diff --git a/views/admin/member-detail.ejs b/views/admin/member-detail.ejs index 4aa5379..df1e5d0 100644 --- a/views/admin/member-detail.ejs +++ b/views/admin/member-detail.ejs @@ -365,6 +365,79 @@ + + +
<%= member.access_token || '–' %>
+
+