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
📊 Server Status
📝 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);
});