// routes/adminPanel.js — password-protected admin panel for free-access shop management const express = require('express'); const crypto = require('crypto'); const { isShopAllowed, addShop, removeShop, listShops } = require('../freeAccessStore'); const router = express.Router(); const ADMIN_USER = 'd4a-admin'; const ADMIN_PASS = 'Data4autos@2026.'; const COOKIE_NAME = 'd4a_admin_tok'; const COOKIE_MAX_AGE = 8 * 60 * 60 * 1000; // 8 hours // In-memory valid tokens (cleared on server restart) const validTokens = new Set(); // ── auth middleware ───────────────────────────────────────────────────────── function requireAuth(req, res, next) { const tok = req.cookies?.[COOKIE_NAME] || req.headers['x-admin-token']; if (tok && validTokens.has(tok)) return next(); if (req.path.startsWith('/api/')) return res.status(401).json({ error: 'Unauthorised' }); res.redirect('/d4a-admin'); } // ── cookie parser (lightweight, no dependency) ────────────────────────────── router.use((req, _res, next) => { const raw = req.headers.cookie || ''; req.cookies = Object.fromEntries( raw.split(';').map(c => c.trim().split('=').map(decodeURIComponent)) .filter(([k]) => k).map(([k, ...v]) => [k, v.join('=')]) ); next(); }); router.use(express.urlencoded({ extended: false })); router.use(express.json()); // ── login ──────────────────────────────────────────────────────────────────── router.post('/login', (req, res) => { const { username, password } = req.body; if (username === ADMIN_USER && password === ADMIN_PASS) { const token = crypto.randomBytes(32).toString('hex'); validTokens.add(token); setTimeout(() => validTokens.delete(token), COOKIE_MAX_AGE); res.setHeader('Set-Cookie', `${COOKIE_NAME}=${encodeURIComponent(token)}; HttpOnly; Path=/; Max-Age=${COOKIE_MAX_AGE / 1000}; SameSite=Lax`); return res.json({ ok: true }); } res.status(401).json({ error: 'Invalid credentials' }); }); // ── logout ─────────────────────────────────────────────────────────────────── router.post('/logout', (req, res) => { const tok = req.cookies?.[COOKIE_NAME]; if (tok) validTokens.delete(tok); res.setHeader('Set-Cookie', `${COOKIE_NAME}=; HttpOnly; Path=/; Max-Age=0; SameSite=Lax`); res.json({ ok: true }); }); // ── check-auth ─────────────────────────────────────────────────────────────── router.get('/api/check-auth', (req, res) => { const tok = req.cookies?.[COOKIE_NAME]; if (tok && validTokens.has(tok)) return res.json({ ok: true }); res.status(401).json({ ok: false }); }); // ── shops API (protected) ──────────────────────────────────────────────────── router.get('/api/shops', requireAuth, (_req, res) => { res.json({ shops: listShops() }); }); router.post('/api/shops', requireAuth, (req, res) => { const { shop, expiresAt, note } = req.body; if (!shop) return res.status(400).json({ error: 'shop is required' }); const entry = addShop(shop.trim(), expiresAt || null, note || ''); res.json({ ok: true, shop: shop.trim(), entry }); }); router.delete('/api/shops/:shop', requireAuth, (req, res) => { removeShop(decodeURIComponent(req.params.shop)); res.json({ ok: true }); }); // ── serve admin HTML (always — login gate is client-side) ──────────────────── router.get('/', (_req, res) => { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.send(adminHtml()); }); // ── HTML ───────────────────────────────────────────────────────────────────── function adminHtml() { return `
Data4Autos Free Access Manager