const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const multer = require('multer'); const fs = require('fs'); const path = require('path'); const axios = require('axios'); const { spawn, exec } = require('child_process'); const chokidar = require('chokidar'); const http = require('http'); const socketIo = require('socket.io'); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"] } }); const MANAGEMENT_PORT = 3000; const MAIN_SERVER_DIR = path.join(__dirname, '..'); const CONFIG_FILE = path.join(__dirname, 'config.json'); // Middleware app.use(helmet({ contentSecurityPolicy: false, // Für WebSocket-Verbindungen })); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, 'public'))); // Multer für File Uploads const upload = multer({ dest: 'uploads/' }); // Default-Konfiguration const defaultConfig = { server: { httpPort: 80, httpsPort: 443, httpsEnabled: true, publicAccess: false, domain: 'localhost' }, ssl: { certPath: '203_cert.pem', keyPath: '203_key.pem', autoRenew: false }, dataSources: [], customTags: [ { id: 'header', name: 'Header', tagName: 'HEADER_CONTENT', enabled: false, content: '', position: 'top', type: 'text' }, { id: 'footer', name: 'Footer', tagName: 'FOOTER_CONTENT', enabled: false, content: '', position: 'bottom', type: 'text' }, { id: 'signature', name: 'Unterschrift', tagName: 'SIGNATURE', enabled: false, content: '', position: 'end', type: 'text' }, { id: 'company_logo', name: 'Firmen-Logo', tagName: 'COMPANY_LOGO', enabled: false, content: '', position: 'header', type: 'image' }, { id: 'watermark', name: 'Wasserzeichen', tagName: 'WATERMARK', enabled: false, content: '', position: 'background', type: 'image' } ], monitoring: { logLevel: 'info', enableMetrics: true, retentionDays: 30 } }; // Konfiguration laden oder erstellen function loadConfig() { try { if (fs.existsSync(CONFIG_FILE)) { const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); return { ...defaultConfig, ...config }; } } catch (error) { console.error('❌ Fehler beim Laden der Konfiguration:', error); } return defaultConfig; } // Konfiguration speichern function saveConfig(config) { try { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); console.log('✅ Konfiguration gespeichert'); return true; } catch (error) { console.error('❌ Fehler beim Speichern der Konfiguration:', error); return false; } } let currentConfig = loadConfig(); // Server-Status-Tracking let serverStatus = { running: false, pid: null, startTime: null, lastRestart: null, requests: 0, errors: 0 }; // WebSocket für Live-Updates io.on('connection', (socket) => { console.log('🔌 Management-Client verbunden'); // Aktuellen Status senden socket.emit('server-status', serverStatus); socket.emit('config-update', currentConfig); socket.on('disconnect', () => { console.log('🔌 Management-Client getrennt'); }); }); // Log-Watcher für Live-Updates function watchServerLogs() { const logFile = path.join(MAIN_SERVER_DIR, 'server.log'); if (fs.existsSync(logFile)) { const watcher = chokidar.watch(logFile); watcher.on('change', () => { try { const logs = fs.readFileSync(logFile, 'utf8').split('\n').slice(-10); io.emit('log-update', logs); } catch (error) { console.error('❌ Fehler beim Lesen der Logs:', error); } }); } } // Server-Status überprüfen function checkServerStatus() { exec('ps aux | grep "node server.js" | grep -v grep', (error, stdout) => { const wasRunning = serverStatus.running; serverStatus.running = !error && stdout.trim().length > 0; if (serverStatus.running && !wasRunning) { const match = stdout.match(/^\S+\s+(\d+)/); serverStatus.pid = match ? parseInt(match[1]) : null; serverStatus.startTime = new Date(); } else if (!serverStatus.running && wasRunning) { serverStatus.pid = null; serverStatus.startTime = null; } io.emit('server-status', serverStatus); }); } // Regelmäßige Status-Updates setInterval(checkServerStatus, 5000); watchServerLogs(); // Routes // Hauptseite - Management Dashboard app.get('/', (req, res) => { res.send(` DOCX Template Server - Management

🚀 DOCX Template Server

Management Dashboard & Configuration

📊 Server Status

Prüfe Status...
--
Uptime
--
Requests
--
PID

📝 Live Logs

Warte auf Logs...

Server-Konfiguration

🔌 Externe Datenquellen

Custom Tags

SSL/TLS Zertifikate

`); }); // API Routes // Server-Steuerung app.post('/api/server/start', (req, res) => { exec(`cd ${MAIN_SERVER_DIR} && nohup node server.js > server.log 2>&1 &`, (error) => { if (error) { console.error('❌ Fehler beim Starten:', error); return res.json({ success: false, message: 'Fehler beim Starten des Servers' }); } serverStatus.lastRestart = new Date(); setTimeout(checkServerStatus, 2000); res.json({ success: true, message: '🚀 Server wird gestartet...' }); }); }); app.post('/api/server/stop', (req, res) => { exec('pkill -f "node server.js"', (error) => { serverStatus.running = false; serverStatus.pid = null; setTimeout(checkServerStatus, 2000); res.json({ success: true, message: '⏹️ Server gestoppt' }); }); }); app.post('/api/server/restart', (req, res) => { exec('pkill -f "node server.js"', () => { setTimeout(() => { exec(`cd ${MAIN_SERVER_DIR} && nohup node server.js > server.log 2>&1 &`, (error) => { serverStatus.lastRestart = new Date(); setTimeout(checkServerStatus, 2000); res.json({ success: true, message: 'Server neu gestartet' }); }); }, 1000); }); }); // Tag-Management APIs app.get('/api/tags', (req, res) => { const config = loadConfig(); res.json(config.customTags || []); }); app.post('/api/tags', (req, res) => { try { const config = loadConfig(); const newTag = req.body; // Validierung if (!newTag.name || !newTag.tagName) { return res.json({ success: false, message: 'Name und Tag-Name sind erforderlich' }); } // Prüfen ob Tag-Name bereits existiert const exists = config.customTags.find(tag => tag.tagName === newTag.tagName); if (exists) { return res.json({ success: false, message: 'Tag-Name bereits vorhanden' }); } if (!config.customTags) config.customTags = []; config.customTags.push(newTag); if (saveConfig(config)) { res.json({ success: true, message: 'Tag erfolgreich erstellt' }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); app.put('/api/tags/:id', (req, res) => { try { const config = loadConfig(); const tagId = req.params.id; const updates = req.body; const tagIndex = config.customTags.findIndex(tag => tag.id === tagId); if (tagIndex === -1) { return res.json({ success: false, message: 'Tag nicht gefunden' }); } // Tag aktualisieren config.customTags[tagIndex] = { ...config.customTags[tagIndex], ...updates }; if (saveConfig(config)) { res.json({ success: true, message: 'Tag aktualisiert' }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); app.post('/api/tags/:id/toggle', (req, res) => { try { const config = loadConfig(); const tagId = req.params.id; const { enabled } = req.body; const tagIndex = config.customTags.findIndex(tag => tag.id === tagId); if (tagIndex === -1) { return res.json({ success: false, message: 'Tag nicht gefunden' }); } config.customTags[tagIndex].enabled = enabled; if (saveConfig(config)) { res.json({ success: true, message: enabled ? 'Tag aktiviert' : 'Tag deaktiviert' }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); app.delete('/api/tags/:id', (req, res) => { try { const config = loadConfig(); const tagId = req.params.id; const tagIndex = config.customTags.findIndex(tag => tag.id === tagId); if (tagIndex === -1) { return res.json({ success: false, message: 'Tag nicht gefunden' }); } config.customTags.splice(tagIndex, 1); if (saveConfig(config)) { res.json({ success: true, message: 'Tag gelöscht' }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); // Public API für externe Tag-Aktivierung app.post('/api/public/tags/:tagName/activate', (req, res) => { try { const config = loadConfig(); const tagName = req.params.tagName.toUpperCase(); const tagIndex = config.customTags.findIndex(tag => tag.tagName === tagName); if (tagIndex === -1) { return res.json({ success: false, message: 'Tag nicht gefunden' }); } config.customTags[tagIndex].enabled = true; if (saveConfig(config)) { res.json({ success: true, message: `Tag ${tagName} aktiviert`, tag: config.customTags[tagIndex] }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); app.post('/api/public/tags/:tagName/deactivate', (req, res) => { try { const config = loadConfig(); const tagName = req.params.tagName.toUpperCase(); const tagIndex = config.customTags.findIndex(tag => tag.tagName === tagName); if (tagIndex === -1) { return res.json({ success: false, message: 'Tag nicht gefunden' }); } config.customTags[tagIndex].enabled = false; if (saveConfig(config)) { res.json({ success: true, message: `Tag ${tagName} deaktiviert`, tag: config.customTags[tagIndex] }); } else { res.json({ success: false, message: 'Fehler beim Speichern' }); } } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); // Liste der verfügbaren Tags für externe Systeme app.get('/api/public/tags', (req, res) => { try { const config = loadConfig(); const publicTags = config.customTags.map(tag => ({ tagName: tag.tagName, name: tag.name, enabled: tag.enabled, type: tag.type, position: tag.position })); res.json(publicTags); } catch (error) { res.json({ success: false, message: 'Server-Fehler: ' + error.message }); } }); // Konfiguration app.get('/api/config', (req, res) => { res.json(currentConfig); }); app.post('/api/config', (req, res) => { try { currentConfig = { ...currentConfig, ...req.body }; if (saveConfig(currentConfig)) { io.emit('config-update', currentConfig); res.json({ success: true, message: '✅ Konfiguration gespeichert' }); } else { res.json({ success: false, message: '❌ Fehler beim Speichern' }); } } catch (error) { res.status(500).json({ success: false, message: error.message }); } }); // SSL-Upload app.post('/api/ssl/upload', upload.fields([ { name: 'cert', maxCount: 1 }, { name: 'key', maxCount: 1 } ]), (req, res) => { try { if (req.files.cert && req.files.key) { const certPath = path.join(MAIN_SERVER_DIR, '203_cert.pem'); const keyPath = path.join(MAIN_SERVER_DIR, '203_key.pem'); fs.copyFileSync(req.files.cert[0].path, certPath); fs.copyFileSync(req.files.key[0].path, keyPath); // Temp-Dateien löschen fs.unlinkSync(req.files.cert[0].path); fs.unlinkSync(req.files.key[0].path); res.json({ success: true, message: '🔒 SSL-Zertifikate hochgeladen' }); } else { res.json({ success: false, message: 'Beide Dateien erforderlich' }); } } catch (error) { res.status(500).json({ success: false, message: error.message }); } }); // Self-Signed Zertifikat generieren app.post('/api/ssl/generate', (req, res) => { const domain = req.body.domain || 'localhost'; const certPath = path.join(MAIN_SERVER_DIR, '203_cert.pem'); const keyPath = path.join(MAIN_SERVER_DIR, '203_key.pem'); const command = `openssl req -x509 -newkey rsa:4096 -keyout "${keyPath}" -out "${certPath}" -days 365 -nodes -subj "/C=DE/ST=State/L=City/O=Organization/CN=${domain}"`; exec(command, (error) => { if (error) { return res.json({ success: false, message: 'OpenSSL nicht verfügbar oder Fehler' }); } res.json({ success: true, message: `🔧 Self-Signed Zertifikat für ${domain} erstellt` }); }); }); // Datenquellen-Management app.get('/api/datasources', (req, res) => { res.json(currentConfig.dataSources || []); }); app.post('/api/datasources', (req, res) => { const { name, type, url, headers, mapping } = req.body; const dataSource = { id: Date.now().toString(), name, type, url, headers: headers || {}, mapping: mapping || {}, enabled: true, created: new Date() }; currentConfig.dataSources = currentConfig.dataSources || []; currentConfig.dataSources.push(dataSource); if (saveConfig(currentConfig)) { res.json({ success: true, message: 'Datenquelle hinzugefügt', dataSource }); } else { res.status(500).json({ success: false, message: 'Fehler beim Speichern' }); } }); // Server starten server.listen(MANAGEMENT_PORT, () => { console.log('\n🎛️ Management-Server gestartet!'); console.log(`📍 Dashboard: http://localhost:${MANAGEMENT_PORT}`); console.log(`🔧 Verwalte deinen DOCX Template Server`); console.log('─'.repeat(50)); // Initialer Status-Check setTimeout(checkServerStatus, 1000); });