🎛️ Web-GUI Management System mit Custom Tags
✨ Neue Features: - Vollständiges Management Dashboard (Port 3000) - Custom Tags System mit REST-API Integration - Mehrere Tags erstellbar und konfigurierbar - Externe Tag-Aktivierung per REST-API - Server-Fernsteuerung (Start/Stop/Restart) - SSL-Zertifikat Management - Echtzeit-Überwachung mit Socket.IO 🏷️ Custom Tags Features: - Dynamische Tag-Erstellung über GUI - Tag-Aktivierung/Deaktivierung per Toggle - REST-APIs für externe Tag-Kontrolle - Integration in Template-Verarbeitung - Konfigurierbare Positionen und Typen 📁 Neue Dateien: - management/ - Komplettes Management-System - API-TAGS-DOCUMENTATION.md - API Dokumentation - start-management.sh - Startup Script 🔧 Verbesserte Template-Verarbeitung: - Automatisches Laden aktivierter Custom Tags - Priorität: Custom-Daten → Custom Tags → Auto-Generierung - Erweiterte Logging und Status-Meldungen 🌐 REST-APIs: - GET /api/public/tags - Alle Tags auflisten - POST /api/public/tags/{TAG_NAME}/activate - Tag aktivieren - POST /api/public/tags/{TAG_NAME}/deactivate - Tag deaktivieren - Management APIs für vollständige CRUD-Operationen
This commit is contained in:
parent
1bd5654f6b
commit
88932bfbf4
139
API-TAGS-DOCUMENTATION.md
Normal file
139
API-TAGS-DOCUMENTATION.md
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# Custom Tags REST API
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
Das Tag-System ermöglicht es, benutzerdefinierte Tags zu erstellen und per REST-API zu aktivieren/deaktivieren.
|
||||||
|
|
||||||
|
## Interne Management APIs (Port 3000)
|
||||||
|
|
||||||
|
### Tags auflisten
|
||||||
|
```
|
||||||
|
GET http://localhost:3000/api/tags
|
||||||
|
```
|
||||||
|
|
||||||
|
### Neuen Tag erstellen
|
||||||
|
```
|
||||||
|
POST http://localhost:3000/api/tags
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "unique_id",
|
||||||
|
"name": "Mein Custom Tag",
|
||||||
|
"tagName": "MY_CUSTOM_TAG",
|
||||||
|
"enabled": false,
|
||||||
|
"content": "Inhalt des Tags",
|
||||||
|
"position": "header",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag aktualisieren
|
||||||
|
```
|
||||||
|
PUT http://localhost:3000/api/tags/{id}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"content": "Neuer Inhalt",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag aktivieren/deaktivieren
|
||||||
|
```
|
||||||
|
POST http://localhost:3000/api/tags/{id}/toggle
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag löschen
|
||||||
|
```
|
||||||
|
DELETE http://localhost:3000/api/tags/{id}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Externe REST APIs (Port 3000)
|
||||||
|
|
||||||
|
### Alle verfügbaren Tags auflisten
|
||||||
|
```
|
||||||
|
GET http://localhost:3000/api/public/tags
|
||||||
|
|
||||||
|
Response:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"tagName": "HEADER_CONTENT",
|
||||||
|
"name": "Header",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "text",
|
||||||
|
"position": "top"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag aktivieren (Extern)
|
||||||
|
```
|
||||||
|
POST http://localhost:3000/api/public/tags/{TAG_NAME}/activate
|
||||||
|
|
||||||
|
Example:
|
||||||
|
POST http://localhost:3000/api/public/tags/HEADER_CONTENT/activate
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Tag HEADER_CONTENT aktiviert",
|
||||||
|
"tag": {
|
||||||
|
"id": "header",
|
||||||
|
"tagName": "HEADER_CONTENT",
|
||||||
|
"enabled": true,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag deaktivieren (Extern)
|
||||||
|
```
|
||||||
|
POST http://localhost:3000/api/public/tags/{TAG_NAME}/deactivate
|
||||||
|
|
||||||
|
Example:
|
||||||
|
POST http://localhost:3000/api/public/tags/HEADER_CONTENT/deactivate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tag-Typen
|
||||||
|
|
||||||
|
- **text**: Einfacher Text-Inhalt
|
||||||
|
- **image**: Bild-Datei (Base64 oder URL)
|
||||||
|
- **html**: HTML-Formatierter Inhalt
|
||||||
|
|
||||||
|
## Positionen
|
||||||
|
|
||||||
|
- **top**: Am Anfang des Dokuments
|
||||||
|
- **bottom**: Am Ende des Dokuments
|
||||||
|
- **header**: Im Kopfbereich
|
||||||
|
- **footer**: Im Fußbereich
|
||||||
|
- **background**: Als Hintergrund/Wasserzeichen
|
||||||
|
- **end**: Ganz am Ende
|
||||||
|
|
||||||
|
## Beispiel-Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tag extern aktivieren
|
||||||
|
curl -X POST http://localhost:3000/api/public/tags/SIGNATURE/activate
|
||||||
|
|
||||||
|
# Tag extern deaktivieren
|
||||||
|
curl -X POST http://localhost:3000/api/public/tags/SIGNATURE/deactivate
|
||||||
|
|
||||||
|
# Alle verfügbaren Tags anzeigen
|
||||||
|
curl http://localhost:3000/api/public/tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## Standard-Tags
|
||||||
|
|
||||||
|
Das System kommt mit diesen vordefinierten Tags:
|
||||||
|
|
||||||
|
1. **HEADER_CONTENT** - Header-Inhalt
|
||||||
|
2. **FOOTER_CONTENT** - Footer-Inhalt
|
||||||
|
3. **SIGNATURE** - Unterschrift
|
||||||
|
4. **COMPANY_LOGO** - Firmen-Logo (Bild)
|
||||||
|
5. **WATERMARK** - Wasserzeichen (Bild)
|
||||||
|
|
||||||
|
Alle Tags können über das Web-GUI oder die REST-APIs verwaltet werden.
|
||||||
75
management/config.json
Normal file
75
management/config.json
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"httpPort": 80,
|
||||||
|
"httpsPort": 443,
|
||||||
|
"domain": "localhost",
|
||||||
|
"publicAccess": true
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"certPath": "203_cert.pem",
|
||||||
|
"keyPath": "203_key.pem",
|
||||||
|
"autoRenew": false
|
||||||
|
},
|
||||||
|
"dataSources": [],
|
||||||
|
"customTags": [
|
||||||
|
{
|
||||||
|
"id": "header",
|
||||||
|
"name": "Header",
|
||||||
|
"tagName": "HEADER_CONTENT",
|
||||||
|
"enabled": true,
|
||||||
|
"content": "=== WICHTIGES DOKUMENT ===\nVertraulich - Nur für interne Verwendung",
|
||||||
|
"position": "top",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "footer",
|
||||||
|
"name": "Footer",
|
||||||
|
"tagName": "FOOTER_CONTENT",
|
||||||
|
"enabled": true,
|
||||||
|
"content": "─────────────────────────\nDieses Dokument wurde automatisch generiert.\nDatum: $(date)\nDOCX Template Server v1.0",
|
||||||
|
"position": "bottom",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "signature",
|
||||||
|
"name": "Unterschrift",
|
||||||
|
"tagName": "SIGNATURE",
|
||||||
|
"enabled": true,
|
||||||
|
"content": "Mit freundlichen Grüßen,\n\nIhr DOCX Template Server\nAutomatisierte Dokumentenerstellung",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom_note",
|
||||||
|
"name": "Spezielle Notiz",
|
||||||
|
"tagName": "CUSTOM_TAG",
|
||||||
|
"enabled": true,
|
||||||
|
"content": "🎯 HINWEIS: Dieses ist ein Custom Tag!\nEs wurde über die REST-API aktiviert.",
|
||||||
|
"position": "middle",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"monitoring": {
|
||||||
|
"logLevel": "info",
|
||||||
|
"enableMetrics": true,
|
||||||
|
"retentionDays": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
270
management/data-sources.js
Normal file
270
management/data-sources.js
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class DataSourceManager {
|
||||||
|
constructor(configPath) {
|
||||||
|
this.configPath = configPath;
|
||||||
|
this.dataSources = this.loadDataSources();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDataSources() {
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
||||||
|
return config.dataSources || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim Laden der Datenquellen:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchFromDataSource(dataSourceId, params = {}) {
|
||||||
|
const dataSource = this.dataSources.find(ds => ds.id === dataSourceId);
|
||||||
|
if (!dataSource || !dataSource.enabled) {
|
||||||
|
throw new Error(`Datenquelle ${dataSourceId} nicht gefunden oder deaktiviert`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🔌 Lade Daten von: ${dataSource.name} (${dataSource.type})`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (dataSource.type) {
|
||||||
|
case 'rest':
|
||||||
|
return await this.fetchFromRest(dataSource, params);
|
||||||
|
case 'database':
|
||||||
|
return await this.fetchFromDatabase(dataSource, params);
|
||||||
|
case 'file':
|
||||||
|
return await this.fetchFromFile(dataSource, params);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unbekannter Datenquellen-Typ: ${dataSource.type}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Fehler bei Datenquelle ${dataSource.name}:`, error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchFromRest(dataSource, params) {
|
||||||
|
const config = {
|
||||||
|
method: dataSource.method || 'GET',
|
||||||
|
url: this.buildUrl(dataSource.url, params),
|
||||||
|
headers: dataSource.headers || {},
|
||||||
|
timeout: dataSource.timeout || 10000
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dataSource.method === 'POST' && params.body) {
|
||||||
|
config.data = params.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios(config);
|
||||||
|
return this.mapResponse(response.data, dataSource.mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchFromDatabase(dataSource, params) {
|
||||||
|
// Hier würde eine DB-Verbindung implementiert werden
|
||||||
|
// Für jetzt simulieren wir es
|
||||||
|
throw new Error('Database-Datenquellen noch nicht implementiert');
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchFromFile(dataSource, params) {
|
||||||
|
const filePath = path.resolve(dataSource.url);
|
||||||
|
const data = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
if (dataSource.format === 'json') {
|
||||||
|
return this.mapResponse(JSON.parse(data), dataSource.mapping);
|
||||||
|
} else if (dataSource.format === 'csv') {
|
||||||
|
// CSV-Parser würde hier implementiert
|
||||||
|
throw new Error('CSV-Format noch nicht implementiert');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { content: data };
|
||||||
|
}
|
||||||
|
|
||||||
|
buildUrl(urlTemplate, params) {
|
||||||
|
let url = urlTemplate;
|
||||||
|
|
||||||
|
// URL-Parameter ersetzen: {param} -> Wert
|
||||||
|
Object.keys(params).forEach(key => {
|
||||||
|
url = url.replace(`{${key}}`, encodeURIComponent(params[key]));
|
||||||
|
});
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapResponse(data, mapping) {
|
||||||
|
if (!mapping || Object.keys(mapping).length === 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapped = {};
|
||||||
|
|
||||||
|
Object.keys(mapping).forEach(targetKey => {
|
||||||
|
const sourcePath = mapping[targetKey];
|
||||||
|
mapped[targetKey] = this.getNestedValue(data, sourcePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNestedValue(obj, path) {
|
||||||
|
return path.split('.').reduce((current, key) => {
|
||||||
|
return current && current[key] !== undefined ? current[key] : null;
|
||||||
|
}, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template-Tags mit Datenquellen erweitern
|
||||||
|
async processDataSourceTags(templateContent, params = {}) {
|
||||||
|
// Finde alle Datenquellen-Tags: {ds:dataSourceId:field}
|
||||||
|
const dsTagRegex = /\{ds:([^:]+):([^}]+)\}/g;
|
||||||
|
const matches = [...templateContent.matchAll(dsTagRegex)];
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
// Gruppiere Tags nach Datenquelle
|
||||||
|
const byDataSource = {};
|
||||||
|
matches.forEach(match => {
|
||||||
|
const [fullTag, dataSourceId, field] = match;
|
||||||
|
if (!byDataSource[dataSourceId]) {
|
||||||
|
byDataSource[dataSourceId] = [];
|
||||||
|
}
|
||||||
|
byDataSource[dataSourceId].push({ fullTag, field });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lade Daten von jeder Datenquelle
|
||||||
|
for (const [dataSourceId, tags] of Object.entries(byDataSource)) {
|
||||||
|
promises.push(
|
||||||
|
this.fetchFromDataSource(dataSourceId, params)
|
||||||
|
.then(sourceData => {
|
||||||
|
tags.forEach(({ fullTag, field }) => {
|
||||||
|
const value = this.getNestedValue(sourceData, field);
|
||||||
|
data[fullTag.slice(1, -1)] = value; // Remove { }
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(`❌ Datenquelle ${dataSourceId} fehlgeschlagen:`, error.message);
|
||||||
|
// Fallback-Werte setzen
|
||||||
|
tags.forEach(({ fullTag, field }) => {
|
||||||
|
data[fullTag.slice(1, -1)] = `[ERROR: ${field}]`;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomTagProcessor {
|
||||||
|
constructor(configPath) {
|
||||||
|
this.configPath = configPath;
|
||||||
|
this.customTags = this.loadCustomTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCustomTags() {
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
||||||
|
return config.customTags || {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim Laden der Custom Tags:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processCustomTags(doc, data) {
|
||||||
|
const processedData = { ...data };
|
||||||
|
|
||||||
|
// Header verarbeiten
|
||||||
|
if (this.customTags.header && this.customTags.header.enabled) {
|
||||||
|
processedData['custom_header'] = this.customTags.header.content;
|
||||||
|
this.insertHeader(doc, this.customTags.header.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer verarbeiten
|
||||||
|
if (this.customTags.footer && this.customTags.footer.enabled) {
|
||||||
|
processedData['custom_footer'] = this.customTags.footer.content;
|
||||||
|
this.insertFooter(doc, this.customTags.footer.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature verarbeiten
|
||||||
|
if (this.customTags.signature && this.customTags.signature.enabled) {
|
||||||
|
processedData['custom_signature'] = this.customTags.signature.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertHeader(doc, content) {
|
||||||
|
try {
|
||||||
|
// Hier würde die Header-Insertion in das DOCX implementiert
|
||||||
|
console.log(`📄 Header hinzugefügt: ${content.substring(0, 50)}...`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim Hinzufügen des Headers:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertFooter(doc, content) {
|
||||||
|
try {
|
||||||
|
// Hier würde die Footer-Insertion in das DOCX implementiert
|
||||||
|
console.log(`📄 Footer hinzugefügt: ${content.substring(0, 50)}...`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim Hinzufügen des Footers:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export für Integration in den Hauptserver
|
||||||
|
module.exports = {
|
||||||
|
DataSourceManager,
|
||||||
|
CustomTagProcessor
|
||||||
|
};
|
||||||
|
|
||||||
|
// Beispiel-Konfiguration für Datenquellen
|
||||||
|
const exampleDataSources = [
|
||||||
|
{
|
||||||
|
id: 'customers_api',
|
||||||
|
name: 'Kunden-API',
|
||||||
|
type: 'rest',
|
||||||
|
url: 'https://api.example.com/customers/{customerId}',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer TOKEN',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
mapping: {
|
||||||
|
'customer_name': 'data.name',
|
||||||
|
'customer_email': 'data.email',
|
||||||
|
'customer_address': 'data.address.street'
|
||||||
|
},
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'products_db',
|
||||||
|
name: 'Produkt-Datenbank',
|
||||||
|
type: 'database',
|
||||||
|
url: 'postgresql://user:pass@localhost:5432/products',
|
||||||
|
query: 'SELECT * FROM products WHERE id = ?',
|
||||||
|
mapping: {
|
||||||
|
'product_name': 'name',
|
||||||
|
'product_price': 'price',
|
||||||
|
'product_description': 'description'
|
||||||
|
},
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'local_data',
|
||||||
|
name: 'Lokale JSON-Datei',
|
||||||
|
type: 'file',
|
||||||
|
url: './data/sample.json',
|
||||||
|
format: 'json',
|
||||||
|
mapping: {
|
||||||
|
'company_info': 'company.name',
|
||||||
|
'contact_person': 'company.contact.name'
|
||||||
|
},
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
];
|
||||||
1011
management/management-server.js
Normal file
1011
management/management-server.js
Normal file
File diff suppressed because it is too large
Load Diff
3497
management/package-lock.json
generated
Normal file
3497
management/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
management/package.json
Normal file
32
management/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "office-server-management",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Web-GUI Management Service für DOCX Template Server",
|
||||||
|
"main": "management-server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node management-server.js",
|
||||||
|
"dev": "nodemon management-server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"helmet": "^7.0.0",
|
||||||
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"axios": "^1.5.0",
|
||||||
|
"pm2": "^5.3.0",
|
||||||
|
"node-cron": "^3.0.2",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"socket.io": "^4.7.2",
|
||||||
|
"chokidar": "^3.5.3"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"docx",
|
||||||
|
"template",
|
||||||
|
"management",
|
||||||
|
"gui",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
"author": "dgsoft",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
62
server.js
62
server.js
@ -12,6 +12,32 @@ const { faker } = require('@faker-js/faker');
|
|||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 80;
|
const PORT = process.env.PORT || 80;
|
||||||
|
|
||||||
|
// Management-System Integration (falls verfügbar)
|
||||||
|
let DataSourceManager, CustomTagProcessor;
|
||||||
|
try {
|
||||||
|
const managementModules = require('./management/data-sources');
|
||||||
|
DataSourceManager = managementModules.DataSourceManager;
|
||||||
|
CustomTagProcessor = managementModules.CustomTagProcessor;
|
||||||
|
console.log('✅ Management-System Integration geladen');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('ℹ️ Management-System nicht verfügbar (optional)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konfiguration laden
|
||||||
|
function loadManagementConfig() {
|
||||||
|
try {
|
||||||
|
const configPath = path.join(__dirname, 'management', 'config.json');
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('ℹ️ Management-Konfiguration nicht verfügbar');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const managementConfig = loadManagementConfig();
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
@ -180,6 +206,33 @@ class DemoDataGenerator {
|
|||||||
|
|
||||||
// Template-Verarbeitung
|
// Template-Verarbeitung
|
||||||
class TemplateProcessor {
|
class TemplateProcessor {
|
||||||
|
// Custom Tags aus Management-System laden
|
||||||
|
static loadCustomTags() {
|
||||||
|
try {
|
||||||
|
const configPath = path.join(__dirname, 'management', 'config.json');
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
console.log('📝 Keine Management-Konfiguration gefunden');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
const customTags = config.customTags || [];
|
||||||
|
const activeTagsData = {};
|
||||||
|
|
||||||
|
customTags
|
||||||
|
.filter(tag => tag.enabled) // Nur aktive Tags
|
||||||
|
.forEach(tag => {
|
||||||
|
activeTagsData[tag.tagName] = tag.content || '';
|
||||||
|
console.log(`✅ Custom Tag aktiviert: ${tag.tagName} = "${tag.content}"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return activeTagsData;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('⚠️ Fehler beim Laden der Custom Tags:', error.message);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static extractTags(content) {
|
static extractTags(content) {
|
||||||
const simpleTags = [...content.matchAll(/\{([^#/}]+)\}/g)].map(match => match[1].trim());
|
const simpleTags = [...content.matchAll(/\{([^#/}]+)\}/g)].map(match => match[1].trim());
|
||||||
const loopTags = [...content.matchAll(/\{#(\w+)\}/g)].map(match => match[1].trim());
|
const loopTags = [...content.matchAll(/\{#(\w+)\}/g)].map(match => match[1].trim());
|
||||||
@ -202,13 +255,20 @@ class TemplateProcessor {
|
|||||||
|
|
||||||
console.log(`📝 Gefundene Tags: ${simpleTags.length} einfache, ${loopTags.length} Schleifen`);
|
console.log(`📝 Gefundene Tags: ${simpleTags.length} einfache, ${loopTags.length} Schleifen`);
|
||||||
|
|
||||||
// Daten zusammenstellen: Custom-Daten haben Priorität, dann Auto-Generierung
|
// Custom Tags aus Management-System laden
|
||||||
|
const customTagsData = this.loadCustomTags();
|
||||||
|
console.log(`🏷️ Custom Tags geladen: ${Object.keys(customTagsData).length} aktive Tags`);
|
||||||
|
|
||||||
|
// Daten zusammenstellen: Custom-Daten haben Priorität, dann Custom Tags, dann Auto-Generierung
|
||||||
let data = {};
|
let data = {};
|
||||||
if (customData && typeof customData === 'object') {
|
if (customData && typeof customData === 'object') {
|
||||||
data = { ...customData };
|
data = { ...customData };
|
||||||
console.log(`🎯 Custom-Daten verwendet: ${Object.keys(customData).length} Felder`);
|
console.log(`🎯 Custom-Daten verwendet: ${Object.keys(customData).length} Felder`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom Tags hinzufügen
|
||||||
|
data = { ...data, ...customTagsData };
|
||||||
|
|
||||||
// Fehlende Tags automatisch generieren
|
// Fehlende Tags automatisch generieren
|
||||||
const missingTags = simpleTags.filter(tag => !(tag in data));
|
const missingTags = simpleTags.filter(tag => !(tag in data));
|
||||||
if (missingTags.length > 0) {
|
if (missingTags.length > 0) {
|
||||||
|
|||||||
55
start-management.sh
Executable file
55
start-management.sh
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "🚀 Starte DOCX Template Server Management System..."
|
||||||
|
|
||||||
|
# Prüfe ob Node.js installiert ist
|
||||||
|
if ! command -v node &> /dev/null; then
|
||||||
|
echo "❌ Node.js ist nicht installiert!"
|
||||||
|
echo "Bitte installiere Node.js: https://nodejs.org/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wechsle ins Management-Verzeichnis
|
||||||
|
cd "$(dirname "$0")/management"
|
||||||
|
|
||||||
|
# Installiere Dependencies wenn node_modules nicht existiert
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
echo "📦 Installiere Dependencies..."
|
||||||
|
npm install
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "❌ Fehler bei der Installation der Dependencies!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe ob PM2 global installiert ist
|
||||||
|
if ! command -v pm2 &> /dev/null; then
|
||||||
|
echo "📦 Installiere PM2 global..."
|
||||||
|
npm install -g pm2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Erstelle Uploads-Verzeichnis
|
||||||
|
mkdir -p uploads
|
||||||
|
|
||||||
|
# Stoppe vorherige Instanzen
|
||||||
|
pm2 delete office-server-management 2>/dev/null || true
|
||||||
|
|
||||||
|
# Starte Management-Server mit PM2
|
||||||
|
echo "🎛️ Starte Management-Dashboard..."
|
||||||
|
pm2 start management-server.js --name "office-server-management" --log-date-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
|
||||||
|
# Zeige Status
|
||||||
|
pm2 status
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Management-System gestartet!"
|
||||||
|
echo "📍 Dashboard: http://localhost:3000"
|
||||||
|
echo "🔧 Verwalte deinen DOCX Template Server über das Web-Interface"
|
||||||
|
echo ""
|
||||||
|
echo "Befehle:"
|
||||||
|
echo " pm2 status - Status anzeigen"
|
||||||
|
echo " pm2 logs office-server-management - Logs anzeigen"
|
||||||
|
echo " pm2 restart office-server-management - Neustart"
|
||||||
|
echo " pm2 stop office-server-management - Stoppen"
|
||||||
|
echo ""
|
||||||
14
templates/custom-tags-template.docx
Normal file
14
templates/custom-tags-template.docx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Hallo {name},
|
||||||
|
|
||||||
|
{HEADER_CONTENT}
|
||||||
|
|
||||||
|
Ihre Daten:
|
||||||
|
- Firma: {firma}
|
||||||
|
- E-Mail: {email}
|
||||||
|
|
||||||
|
{CUSTOM_TAG}
|
||||||
|
|
||||||
|
Mit freundlichen Grüßen,
|
||||||
|
{SIGNATURE}
|
||||||
|
|
||||||
|
{FOOTER_CONTENT}
|
||||||
21
templates/test-custom-tags.txt
Normal file
21
templates/test-custom-tags.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Sehr geehrte Damen und Herren,
|
||||||
|
|
||||||
|
{HEADER_CONTENT}
|
||||||
|
|
||||||
|
Hiermit bestätigen wir folgende Daten:
|
||||||
|
|
||||||
|
Name: {name}
|
||||||
|
Firma: {firma}
|
||||||
|
E-Mail: {email}
|
||||||
|
Telefon: {telefon}
|
||||||
|
|
||||||
|
{CUSTOM_TAG}
|
||||||
|
|
||||||
|
Mit freundlichen Grüßen,
|
||||||
|
|
||||||
|
{SIGNATURE}
|
||||||
|
|
||||||
|
{FOOTER_CONTENT}
|
||||||
|
|
||||||
|
---
|
||||||
|
{WATERMARK}
|
||||||
Loading…
x
Reference in New Issue
Block a user