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 } ];