const express = require('express'); const https = require('https'); const fs = require('fs'); const path = require('path'); const cors = require('cors'); const AdmZip = require('adm-zip'); const Docxtemplater = require('docxtemplater'); const PizZip = require('pizzip'); const app = express(); const randomData = { names: ['Max Mustermann', 'Anna Schmidt', 'Tom Weber', 'Lisa KΓΆnig', 'Jan Peters'], emails: ['max@example.com', 'anna@example.com', 'tom@example.com', 'lisa@example.com'], products: ['Premium Service', 'Standard LΓΆsung', 'Enterprise Paket'], companies: ['TechCorp GmbH', 'Innovation AG', 'Digital Solutions'] }; function randomChoice(array) { return array[Math.floor(Math.random() * array.length)]; } function generateRandomValue(tag) { const tagLower = tag.toLowerCase(); if (tagLower.includes('name')) return randomChoice(randomData.names); if (tagLower.includes('email')) return randomChoice(randomData.emails); if (tagLower.includes('datum')) return new Date().toLocaleDateString('de-DE'); if (tagLower.includes('projekt')) return 'Dynamisches Projekt ' + Math.floor(Math.random() * 1000); if (tagLower.includes('status')) return randomChoice(['Aktiv', 'In Bearbeitung', 'Abgeschlossen']); return `[${tag}]`; } function generateTableData(tableName, rowCount = 3) { const tableData = []; for (let i = 0; i < rowCount; i++) { if (tableName.toLowerCase().includes('mitarbeiter')) { tableData.push({ nr: i + 1, name: randomChoice(randomData.names), position: randomChoice(['Manager', 'Developer', 'Designer']), email: randomChoice(randomData.emails) }); } else { tableData.push({ nr: i + 1, bezeichnung: `Eintrag ${i + 1}`, wert: (Math.random() * 1000).toFixed(2) }); } } return tableData; } function extractTemplateTagsFromDocx(filePath) { try { const zip = new AdmZip(filePath); const documentXml = zip.readAsText('word/document.xml'); const tags = new Set(); const tableNames = new Set(); const simpleTagRegex = /\{([^{}#\/]+)\}/g; let match; while ((match = simpleTagRegex.exec(documentXml)) !== null) { if (!match[1].startsWith('#') && !match[1].startsWith('/')) { tags.add(match[1]); } } const loopTagRegex = /\{#([^{}]+)\}/g; while ((match = loopTagRegex.exec(documentXml)) !== null) { tableNames.add(match[1]); } return { simpleTags: Array.from(tags), tableNames: Array.from(tableNames), tagCount: tags.size + tableNames.size }; } catch (error) { return { simpleTags: [], tableNames: [], tagCount: 0 }; } } app.use(cors()); app.use(express.json()); app.use(express.static('public')); const templatesDir = path.join(__dirname, 'templates'); const outputDir = path.join(__dirname, 'output'); [templatesDir, outputDir].forEach(dir => { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } }); let httpsOptions = null; const certPath = path.join(__dirname, 'ssl', 'cert.pem'); const keyPath = path.join(__dirname, 'ssl', 'key.pem'); if (fs.existsSync(certPath) && fs.existsSync(keyPath)) { httpsOptions = { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) }; console.log('πŸ”’ SSL-Zertifikate gefunden und geladen'); } // NEUE DOCXTEMPLATER ROUTEN app.get('/analyze/:templateName', (req, res) => { try { const templateName = req.params.templateName; const templatePath = path.join(templatesDir, templateName); if (!fs.existsSync(templatePath)) { return res.status(404).json({ error: `Template ${templateName} nicht gefunden` }); } const analysis = extractTemplateTagsFromDocx(templatePath); res.json({ template: templateName, analysis, syntax: 'docxtemplater', examples: { simple: 'Einfache Tags: {name}, {datum}', tables: 'Tabellen: {#mitarbeiter}{name}{/mitarbeiter}' } }); } catch (error) { res.status(500).json({ error: 'Analyse-Fehler', details: error.message }); } }); app.post('/generate/:templateName', async (req, res) => { try { const templateName = req.params.templateName; const { tables = {}, data = {} } = req.body; console.log(`πŸ”„ Generiere: ${templateName}`); const templatePath = path.join(templatesDir, templateName); if (!fs.existsSync(templatePath)) { return res.status(404).json({ error: `Template nicht gefunden` }); } const content = fs.readFileSync(templatePath, 'binary'); const zip = new PizZip(content); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true }); const templateData = { ...data }; const analysis = extractTemplateTagsFromDocx(templatePath); analysis.simpleTags.forEach(tag => { if (!templateData[tag]) { templateData[tag] = generateRandomValue(tag); } }); Object.entries(tables).forEach(([tableName, rowCount]) => { console.log(`πŸ“Š ${tableName}: ${rowCount} Zeilen`); templateData[tableName] = generateTableData(tableName, parseInt(rowCount)); }); doc.render(templateData); const buffer = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' }); const timestamp = Date.now(); const outputFileName = `${templateName.replace('.docx', '')}_${timestamp}.docx`; const outputPath = path.join(outputDir, outputFileName); fs.writeFileSync(outputPath, buffer); res.json({ success: true, message: `βœ… Dokument erstellt`, file: outputFileName, download: `/download/${outputFileName}` }); } catch (error) { console.error('Fehler:', error); res.status(500).json({ error: 'Generierungs-Fehler', details: error.message }); } }); app.post('/demo/:templateName', async (req, res) => { try { const templateName = req.params.templateName; const { tables = {} } = req.body; console.log(`πŸŽͺ Demo: ${templateName}`); const templatePath = path.join(templatesDir, templateName); if (!fs.existsSync(templatePath)) { return res.status(404).json({ error: `Template nicht gefunden` }); } const analysis = extractTemplateTagsFromDocx(templatePath); console.log(`πŸ” ${analysis.simpleTags.length} Tags, ${analysis.tableNames.length} Tabellen`); const autoTables = { ...tables }; analysis.tableNames.forEach(tableName => { if (!autoTables[tableName]) { autoTables[tableName] = Math.floor(Math.random() * 5) + 3; } }); const content = fs.readFileSync(templatePath, 'binary'); const zip = new PizZip(content); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true }); const templateData = {}; analysis.simpleTags.forEach(tag => { templateData[tag] = generateRandomValue(tag); }); Object.entries(autoTables).forEach(([tableName, rowCount]) => { templateData[tableName] = generateTableData(tableName, parseInt(rowCount)); }); doc.render(templateData); const buffer = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' }); const timestamp = Date.now(); const outputFileName = `demo_${templateName.replace('.docx', '')}_${timestamp}.docx`; const outputPath = path.join(outputDir, outputFileName); fs.writeFileSync(outputPath, buffer); res.json({ success: true, message: `πŸŽ‰ Demo erstellt`, file: outputFileName, download: `/download/${outputFileName}`, analysis: { simpleTags: analysis.simpleTags, tables: Object.keys(autoTables) } }); } catch (error) { res.status(500).json({ error: 'Demo-Fehler', details: error.message }); } }); // UTILITY ROUTEN app.get('/download/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(outputDir, filename); if (fs.existsSync(filePath)) { res.download(filePath); } else { res.status(404).json({ error: 'Datei nicht gefunden' }); } }); app.get('/templates', (req, res) => { try { const files = fs.readdirSync(templatesDir) .filter(file => file.endsWith('.docx')) .map(file => { const templatePath = path.join(templatesDir, file); const stats = fs.statSync(templatePath); const analysis = extractTemplateTagsFromDocx(templatePath); return { name: file, size: stats.size, modified: stats.mtime, tags: analysis.tagCount, simpleTags: analysis.simpleTags.length, tables: analysis.tableNames.length, analyzeUrl: `/analyze/${file}`, generateUrl: `/generate/${file}`, demoUrl: `/demo/${file}` }; }); res.json({ templates: files, count: files.length, syntax: 'docxtemplater', info: 'Tags: {tag}, Loops: {#table}{field}{/table}' }); } catch (error) { res.status(500).json({ error: 'Template-Fehler', details: error.message }); } }); app.get('/files', (req, res) => { try { const files = fs.readdirSync(outputDir) .filter(file => file.endsWith('.docx')) .map(file => { const filePath = path.join(outputDir, file); const stats = fs.statSync(filePath); return { name: file, size: stats.size, created: stats.birthtime, download: `/download/${file}` }; }) .sort((a, b) => new Date(b.created) - new Date(a.created)); res.json({ files, count: files.length }); } catch (error) { res.status(500).json({ error: 'Dateien-Fehler', details: error.message }); } }); app.get('/health', (req, res) => { res.json({ status: 'OK', engine: 'docxtemplater', timestamp: new Date().toISOString(), templates: fs.readdirSync(templatesDir).filter(f => f.endsWith('.docx')).length, outputs: fs.readdirSync(outputDir).filter(f => f.endsWith('.docx')).length }); }); app.use('/webdav/templates', express.static(templatesDir)); app.use('/webdav/output', express.static(outputDir)); // SERVER STARTEN app.listen(3000, () => { console.log('πŸš€ DOCX Template Server (docxtemplater) lΓ€uft auf Port 3000'); console.log('πŸ“ Templates:', templatesDir); console.log('πŸ“„ Output:', outputDir); console.log('🌐 HTTP: http://localhost:3000'); console.log('πŸ“‚ WebDAV: http://localhost:3000/webdav/'); }); if (httpsOptions) { https.createServer(httpsOptions, app).listen(3443, () => { console.log('πŸ”’ DOCX Template Server (docxtemplater) lΓ€uft auf Port 3443'); console.log('🌐 HTTPS: https://localhost:3443'); console.log('πŸ“‚ WebDAV: https://localhost:3443/webdav/'); console.log('πŸ” SSL aktiv'); }); }