Move Projekt-KIQ-HP bahn/ -> dhive/ (dhive project); add steering files
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import crypto from 'crypto';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DATA_FILE = join(__dirname, 'data', 'access-requests.json');
|
||||
|
||||
// --- Config ---
|
||||
const PORT = process.env.PORT || 3003;
|
||||
const ADMIN_USER = process.env.ADMIN_USER || 'admin';
|
||||
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'changeme';
|
||||
const SESSION_SECRET = process.env.SESSION_SECRET || 'dev-secret-change-in-production';
|
||||
const SMTP_HOST = process.env.SMTP_HOST;
|
||||
const SMTP_PORT = parseInt(process.env.SMTP_PORT || '587', 10);
|
||||
const SMTP_USER = process.env.SMTP_USER;
|
||||
const SMTP_PASS = process.env.SMTP_PASS;
|
||||
const ALERT_EMAIL = process.env.ALERT_EMAIL;
|
||||
|
||||
// --- Data helpers ---
|
||||
function loadRequests() {
|
||||
if (!existsSync(DATA_FILE)) return [];
|
||||
try {
|
||||
return JSON.parse(readFileSync(DATA_FILE, 'utf-8'));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveRequests(requests) {
|
||||
writeFileSync(DATA_FILE, JSON.stringify(requests, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
// Ensure data directory exists
|
||||
import { mkdirSync } from 'fs';
|
||||
mkdirSync(join(__dirname, 'data'), { recursive: true });
|
||||
|
||||
// --- Session store (in-memory, simple) ---
|
||||
const sessions = new Map();
|
||||
|
||||
function createSession(user) {
|
||||
const token = crypto.randomBytes(32).toString('hex');
|
||||
sessions.set(token, { user, createdAt: Date.now() });
|
||||
return token;
|
||||
}
|
||||
|
||||
function getSession(token) {
|
||||
return sessions.get(token) || null;
|
||||
}
|
||||
|
||||
function destroySession(token) {
|
||||
sessions.delete(token);
|
||||
}
|
||||
|
||||
// --- Auth middleware ---
|
||||
function requireAuth(req, res, next) {
|
||||
const token = req.cookies?.kiq_session;
|
||||
if (!token) return res.status(401).json({ error: 'Nicht authentifiziert' });
|
||||
const session = getSession(token);
|
||||
if (!session) return res.status(401).json({ error: 'Sitzung abgelaufen' });
|
||||
req.user = session.user;
|
||||
next();
|
||||
}
|
||||
|
||||
// --- Email helper ---
|
||||
async function sendAlertEmail(requestData) {
|
||||
if (!SMTP_HOST || !SMTP_USER || !SMTP_PASS || !ALERT_EMAIL) {
|
||||
console.log('[Email] SMTP not configured, skipping alert email');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: SMTP_HOST,
|
||||
port: SMTP_PORT,
|
||||
secure: SMTP_PORT === 465,
|
||||
auth: { user: SMTP_USER, pass: SMTP_PASS },
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: SMTP_USER,
|
||||
to: ALERT_EMAIL,
|
||||
subject: `[KIQ] Neue Zugriffsanfrage von ${requestData.name}`,
|
||||
text: [
|
||||
`Neue Zugriffsanfrage eingegangen:`,
|
||||
``,
|
||||
`Name: ${requestData.name}`,
|
||||
`Unternehmen: ${requestData.company}`,
|
||||
`Rolle: ${requestData.role}`,
|
||||
`E-Mail: ${requestData.email}`,
|
||||
`Telefon: ${requestData.phone}`,
|
||||
`Gesprächsziel: ${requestData.purpose}`,
|
||||
`Motivation: ${requestData.reason}`,
|
||||
``,
|
||||
`Zeitpunkt: ${new Date().toLocaleString('de-DE')}`,
|
||||
``,
|
||||
`→ Admin-Dashboard: https://projekt-kiq.d-hive.de/admin`,
|
||||
].join('\n'),
|
||||
});
|
||||
console.log('[Email] Alert sent to', ALERT_EMAIL);
|
||||
} catch (err) {
|
||||
console.error('[Email] Failed to send alert:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Express app ---
|
||||
const app = express();
|
||||
|
||||
app.use(cors({
|
||||
origin: process.env.NODE_ENV === 'production'
|
||||
? 'https://projekt-kiq.d-hive.de'
|
||||
: 'http://localhost:5173',
|
||||
credentials: true,
|
||||
}));
|
||||
app.use(express.json());
|
||||
app.use(cookieParser(SESSION_SECRET));
|
||||
|
||||
// --- Routes ---
|
||||
|
||||
// Health check
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({ status: 'ok', time: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Login
|
||||
app.post('/api/login', (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if (username === ADMIN_USER && password === ADMIN_PASSWORD) {
|
||||
const token = createSession({ username, role: 'admin' });
|
||||
res.cookie('kiq_session', token, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
});
|
||||
return res.json({ user: { username, role: 'admin' } });
|
||||
}
|
||||
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
|
||||
});
|
||||
|
||||
// Logout
|
||||
app.post('/api/logout', (req, res) => {
|
||||
const token = req.cookies?.kiq_session;
|
||||
if (token) destroySession(token);
|
||||
res.clearCookie('kiq_session');
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// Current user
|
||||
app.get('/api/me', (req, res) => {
|
||||
const token = req.cookies?.kiq_session;
|
||||
if (!token) return res.json({ user: null });
|
||||
const session = getSession(token);
|
||||
if (!session) return res.json({ user: null });
|
||||
res.json({ user: session.user });
|
||||
});
|
||||
|
||||
// Submit access request (public)
|
||||
app.post('/api/access-requests', async (req, res) => {
|
||||
const { user_data } = req.body;
|
||||
if (!user_data || !user_data.name || !user_data.email || !user_data.company) {
|
||||
return res.status(400).json({ error: 'Pflichtfelder fehlen' });
|
||||
}
|
||||
|
||||
const requests = loadRequests();
|
||||
const newRequest = {
|
||||
id: crypto.randomUUID(),
|
||||
user_data,
|
||||
status: 'pending',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
requests.push(newRequest);
|
||||
saveRequests(requests);
|
||||
|
||||
// Send email alert (non-blocking)
|
||||
sendAlertEmail(user_data);
|
||||
|
||||
res.status(201).json({ ok: true, id: newRequest.id });
|
||||
});
|
||||
|
||||
// Get all access requests (admin only)
|
||||
app.get('/api/access-requests', requireAuth, (_req, res) => {
|
||||
const requests = loadRequests();
|
||||
// Sort newest first
|
||||
requests.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||
res.json(requests);
|
||||
});
|
||||
|
||||
// Update access request status (admin only)
|
||||
app.put('/api/access-requests/:id', requireAuth, (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
if (!['pending', 'approved', 'rejected'].includes(status)) {
|
||||
return res.status(400).json({ error: 'Ungültiger Status' });
|
||||
}
|
||||
|
||||
const requests = loadRequests();
|
||||
const idx = requests.findIndex(r => r.id === id);
|
||||
if (idx === -1) return res.status(404).json({ error: 'Anfrage nicht gefunden' });
|
||||
|
||||
requests[idx].status = status;
|
||||
saveRequests(requests);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// --- Start ---
|
||||
app.listen(PORT, () => {
|
||||
console.log(`[KIQ Backend] Running on port ${PORT}`);
|
||||
});
|
||||
Reference in New Issue
Block a user