const express = require('express'); const Docxtemplater = require('docxtemplater'); const PizZip = require('pizzip'); const fs = require('fs'); const path = require('path'); const multer = require('multer'); const cors = require('cors'); const helmet = require('helmet'); const https = require('https'); const crypto = require('crypto'); const { faker } = require('@faker-js/faker'); const app = express(); const PORT = process.env.PORT || 80; // Middleware app.use(helmet()); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Verzeichnisse erstellen const templateDir = path.join(__dirname, 'templates'); const outputDir = path.join(__dirname, 'documents'); [templateDir, outputDir].forEach(dir => { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } }); // WebDAV-Implementierung für Word/Office-Integration mit SharePoint-Kompatibilität const webdavMethods = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK']; // SharePoint-spezifische Namespaces und Header const sharePointNamespaces = { 'xmlns:D': 'DAV:', 'xmlns:S': 'http://schemas.microsoft.com/sharepoint/soap/', 'xmlns:Z': 'urn:schemas-microsoft-com:', 'xmlns:O': 'urn:schemas-microsoft-com:office:office', 'xmlns:T': 'http://schemas.microsoft.com/repl/' }; // WebDAV PROPFIND Handler mit SharePoint-Modus app.use('/webdav', (req, res, next) => { // Erweiterte CORS und WebDAV-Header für Office/SharePoint res.set({ 'DAV': '1, 2, ordered-collections, versioning, extended-mkcol', 'MS-Author-Via': 'DAV', 'Server': 'Microsoft-IIS/10.0 Microsoft-HTTPAPI/2.0', 'MicrosoftSharePointTeamServices': '15.0.0.4569', 'SPRequestGuid': `{${crypto.randomUUID()}}`, 'SPIisLatency': '0', 'Allow': 'GET, HEAD, POST, PUT, DELETE, OPTIONS, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, DELETE, OPTIONS, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK', 'Access-Control-Allow-Headers': 'Content-Type, Depth, Authorization, Destination, If, Lock-Token, Overwrite, Timeout, X-Requested-With, SOAPAction', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'X-SharePointHealthScore': '0', 'X-AspNet-Version': '4.0.30319', 'X-Powered-By': 'ASP.NET' }); if (req.method === 'OPTIONS') { return res.status(200).end(); } if (req.method === 'PROPFIND') { const depth = req.headers.depth || 'infinity'; const requestPath = req.path.replace('/webdav', ''); let targetDir; if (requestPath.startsWith('/templates')) { targetDir = path.join(templateDir, requestPath.replace('/templates', '')); } else if (requestPath.startsWith('/documents')) { targetDir = path.join(outputDir, requestPath.replace('/documents', '')); } else { targetDir = __dirname; } try { let items = []; if (fs.existsSync(targetDir)) { const stats = fs.statSync(targetDir); if (stats.isDirectory()) { const files = fs.readdirSync(targetDir); // Verzeichnis selbst items.push({ href: req.path + (req.path.endsWith('/') ? '' : '/'), isDirectory: true, lastModified: stats.mtime.toUTCString(), size: 0 }); // Dateien im Verzeichnis files.forEach(file => { const filePath = path.join(targetDir, file); const fileStats = fs.statSync(filePath); items.push({ href: req.path + (req.path.endsWith('/') ? '' : '/') + file, isDirectory: fileStats.isDirectory(), lastModified: fileStats.mtime.toUTCString(), size: fileStats.size || 0, contentType: fileStats.isDirectory() ? 'httpd/unix-directory' : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); }); } else { // Einzelne Datei items.push({ href: req.path, isDirectory: false, lastModified: stats.mtime.toUTCString(), size: stats.size, contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); } } // WebDAV XML Response mit SharePoint-spezifischen Eigenschaften const xmlResponse = ` ${items.map(item => ` ${item.href} ${path.basename(item.href)} ${item.lastModified} ${item.size} ${item.contentType} ${item.isDirectory ? '' : ''} "${Date.now()}-${item.size}" ${new Date(item.lastModified).toISOString()} ${item.isDirectory ? 'true' : 'false'} F F F F ${new Date(item.lastModified).toISOString()} ${new Date().toISOString()} ${new Date(item.lastModified).toISOString()} 00000020 System Account 0 1 0x0101 0 HTTP/1.1 200 OK `).join('')} `; res.set('Content-Type', 'application/xml; charset=utf-8'); res.status(207).send(xmlResponse); } catch (error) { res.status(404).send('Not Found'); } return; } next(); }); // WebDAV PUT Handler für Dateien speichern // PUT - File Upload (für Word Speichern) app.put('/dav/*', (req, res) => { const filePath = path.join(__dirname, 'uploads', req.params[0]); const dirPath = path.dirname(filePath); console.log(`� PUT Request: ${req.params[0]}`); console.log(`📂 Speichere nach: ${filePath}`); // Stelle sicher dass der Ordner existiert fs.mkdirSync(dirPath, { recursive: true }); const writeStream = fs.createWriteStream(filePath); req.pipe(writeStream); writeStream.on('finish', () => { console.log(`✅ Datei gespeichert: ${filePath}`); res.set({ 'DAV': '1, 2, ordered-collections, versioning, extended-mkcol', 'MS-Author-Via': 'DAV', 'Server': 'Microsoft-IIS/10.0 Microsoft-HTTPAPI/2.0', 'MicrosoftSharePointTeamServices': '15.0.0.4569', 'SPRequestGuid': `{${crypto.randomUUID()}}`, 'Cache-Control': 'no-cache', 'ETag': `"${Date.now()}-${req.headers['content-length'] || 0}"`, 'Last-Modified': new Date().toUTCString(), 'X-SharePoint-HealthScore': '0' }); res.status(201).send('Created'); }); writeStream.on('error', (err) => { console.error(`❌ Fehler beim Speichern: ${err.message}`); res.status(500).send('Internal Server Error'); }); }); app.put('/webdav/documents/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(outputDir, filename); console.log(`📝 Word speichert Dokument: ${filename}`); const writeStream = fs.createWriteStream(filePath); req.pipe(writeStream); writeStream.on('finish', () => { // Dateirechte explizit setzen try { fs.chmodSync(filePath, 0o666); // Lese-/Schreibrechte für alle } catch (error) { console.warn('Warnung: Konnte Dateiberechtigungen nicht setzen:', error.message); } res.set({ 'DAV': '1, 2, ordered-collections, versioning', 'MS-Author-Via': 'DAV', 'Server': 'Microsoft-IIS/10.0', 'Cache-Control': 'no-cache', 'ETag': `"${Date.now()}-${req.headers['content-length'] || 0}"`, 'Last-Modified': new Date().toUTCString() }); res.status(201).send('Created'); console.log(`✅ Dokument gespeichert: ${filename}`); }); writeStream.on('error', (error) => { console.error('❌ Fehler beim Speichern:', error); res.status(500).send('Internal Server Error'); }); }); // WebDAV GET Handler für Dateien lesen app.get('/webdav/templates/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(templateDir, filename); if (fs.existsSync(filePath)) { const stats = fs.statSync(filePath); res.set({ 'DAV': '1, 2, ordered-collections, versioning', 'MS-Author-Via': 'DAV', 'Server': 'Microsoft-IIS/10.0', 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'Content-Length': stats.size, 'Last-Modified': stats.mtime.toUTCString(), 'ETag': `"${stats.mtime.getTime()}-${stats.size}"`, 'Accept-Ranges': 'bytes', 'Cache-Control': 'no-cache' }); console.log(`📖 Word öffnet Template: ${filename}`); res.sendFile(filePath); } else { res.status(404).send('Not Found'); } }); app.get('/webdav/documents/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(outputDir, filename); if (fs.existsSync(filePath)) { const stats = fs.statSync(filePath); res.set({ 'DAV': '1, 2, ordered-collections, versioning', 'MS-Author-Via': 'DAV', 'Server': 'Microsoft-IIS/10.0', 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'Content-Length': stats.size, 'Last-Modified': stats.mtime.toUTCString(), 'ETag': `"${stats.mtime.getTime()}-${stats.size}"`, 'Accept-Ranges': 'bytes', 'Cache-Control': 'no-cache' }); console.log(`📖 Word öffnet Dokument: ${filename}`); res.sendFile(filePath); } else { res.status(404).send('Not Found'); } }); // WebDAV LOCK/UNLOCK für Office app.use('/webdav', (req, res, next) => { if (req.method === 'LOCK') { const lockToken = 'opaquelocktoken:' + Date.now() + '-' + Math.random().toString(36).substr(2, 9); const lockResponse = ` 0 Second-604800 ${lockToken} `; res.set({ 'Content-Type': 'application/xml; charset=utf-8', 'Lock-Token': `<${lockToken}>`, 'DAV': '1, 2', 'MS-Author-Via': 'DAV' }); res.status(200).send(lockResponse); return; } if (req.method === 'UNLOCK') { res.set({ 'DAV': '1, 2', 'MS-Author-Via': 'DAV' }); res.status(204).send(); return; } next(); }); app.use('/webdav/documents', express.static(outputDir, { setHeaders: (res, path) => { res.set({ 'DAV': '1, 2', 'Allow': 'GET, PUT, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, HEAD, OPTIONS', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, PUT, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, HEAD, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Depth, Authorization, If-Match, If-None-Match, Overwrite, Destination', 'MS-Author-Via': 'DAV', 'Cache-Control': 'no-cache' }); } })); // WebDAV PROPFIND Support für Word app.use('/webdav/*', (req, res, next) => { if (req.method === 'PROPFIND') { res.set({ 'Content-Type': 'application/xml; charset=utf-8', 'DAV': '1, 2', 'MS-Author-Via': 'DAV' }); const propfindResponse = ` ${req.originalUrl} HTTP/1.1 200 OK httpd/unix-directory `; res.status(207).send(propfindResponse); return; } next(); }); // SharePoint-spezifische Endpunkte app.get('/_vti_inf.html', (req, res) => { // SharePoint Info-Seite res.set('Content-Type', 'text/html'); res.send(``); }); app.post('/_vti_bin/lists.asmx', (req, res) => { // SharePoint Lists Web Service res.set({ 'Content-Type': 'text/xml; charset=utf-8', 'SOAPAction': req.headers.soapaction || '' }); const soapResponse = ` `; res.send(soapResponse); }); app.get('/_vti_bin/owssvr.dll', (req, res) => { // SharePoint OWS Service if (req.query.Cmd === 'Display' && req.query.List) { res.set('Content-Type', 'text/xml'); res.send(` `); } else { res.status(404).send('Not Found'); } }); // SharePoint-kompatible WebDAV-Root app.use('/sharepoint', (req, res, next) => { // SharePoint-spezifische Header res.set({ 'MicrosoftSharePointTeamServices': '15.0.0.4569', 'SharePointHealthScore': '0', 'SPRequestGuid': `{${crypto.randomUUID()}}`, 'X-SharePoint-HealthScore': '0' }); // Weiterleitung zu WebDAV const newPath = req.path.replace('/sharepoint', '/webdav'); req.url = newPath; next(); }); console.log('SharePoint-Kompatibilitätsmodus aktiviert:'); console.log('SharePoint WebDAV: http://localhost:' + (process.env.PORT || 80) + '/sharepoint/templates/'); console.log('SharePoint Info: http://localhost:' + (process.env.PORT || 80) + '/_vti_inf.html'); // Multer für File Upload const upload = multer({ dest: 'uploads/' }); // Demo-Daten Generator class DemoDataGenerator { static generateData(tags) { const data = {}; tags.forEach(tag => { const lowerTag = tag.toLowerCase(); if (lowerTag.includes('name') || lowerTag.includes('vorname')) { data[tag] = faker.person.firstName(); } else if (lowerTag.includes('nachname') || lowerTag.includes('surname')) { data[tag] = faker.person.lastName(); } else if (lowerTag.includes('email') || lowerTag.includes('mail')) { data[tag] = faker.internet.email(); } else if (lowerTag.includes('telefon') || lowerTag.includes('phone')) { data[tag] = faker.phone.number(); } else if (lowerTag.includes('adresse') || lowerTag.includes('address')) { data[tag] = faker.location.streetAddress(); } else if (lowerTag.includes('stadt') || lowerTag.includes('city')) { data[tag] = faker.location.city(); } else if (lowerTag.includes('plz') || lowerTag.includes('postal')) { data[tag] = faker.location.zipCode(); } else if (lowerTag.includes('land') || lowerTag.includes('country')) { data[tag] = faker.location.country(); } else if (lowerTag.includes('datum') || lowerTag.includes('date')) { data[tag] = faker.date.recent().toLocaleDateString('de-DE'); } else if (lowerTag.includes('betrag') || lowerTag.includes('preis') || lowerTag.includes('amount')) { data[tag] = faker.commerce.price(); } else if (lowerTag.includes('firma') || lowerTag.includes('company')) { data[tag] = faker.company.name(); } else if (lowerTag.includes('produkt') || lowerTag.includes('product')) { data[tag] = faker.commerce.productName(); } else if (lowerTag.includes('beschreibung') || lowerTag.includes('description')) { data[tag] = faker.lorem.paragraph(); } else if (lowerTag.includes('nummer') || lowerTag.includes('number') || lowerTag.includes('id')) { data[tag] = faker.string.numeric(6); } else { // Fallback für unbekannte Tags data[tag] = faker.lorem.words(2); } }); return data; } static generateTableData(tableStructure) { const rows = Math.floor(Math.random() * 5) + 2; // 2-6 Zeilen const tableData = []; for (let i = 0; i < rows; i++) { const row = {}; tableStructure.forEach(column => { if (column.includes('position')) { row[column] = (i + 1).toString(); // Fortlaufende Positionsnummer } else { row[column] = this.generateData([column])[column]; } }); tableData.push(row); } return tableData; } } // Template Tag Extractor class TemplateTagExtractor { static extractTags(docxBuffer) { try { const zip = new PizZip(docxBuffer); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, }); // Alle Tags aus dem Template extrahieren const tags = new Set(); const content = zip.file('word/document.xml').asText(); // Einfache Tags: {tag} const simpleTagRegex = /{([^{}]+)}/g; let match; while ((match = simpleTagRegex.exec(content)) !== null) { const tag = match[1].trim(); if (!tag.includes('#') && !tag.includes('/')) { tags.add(tag); } } // Loop Tags für Tabellen: {#items}...{/items} const loopTagRegex = /{#(\w+)}/g; const loopTags = []; while ((match = loopTagRegex.exec(content)) !== null) { loopTags.push(match[1]); } return { simpleTags: Array.from(tags), loopTags: loopTags }; } catch (error) { console.error('Fehler beim Extrahieren der Tags:', error); return { simpleTags: [], loopTags: [] }; } } } // Template Processor class TemplateProcessor { static async processTemplate(templatePath, outputPath, customData = null) { try { const content = fs.readFileSync(templatePath, 'binary'); const zip = new PizZip(content); // Tags extrahieren const { simpleTags, loopTags } = TemplateTagExtractor.extractTags(Buffer.from(content, 'binary')); // Daten generieren oder verwenden let data = customData || {}; if (!customData) { // Demo-Daten für einfache Tags generieren data = DemoDataGenerator.generateData(simpleTags); // Demo-Daten für Loop Tags (Tabellen) generieren loopTags.forEach(loopTag => { // Erweiterte Tabellen-Spalten für professionelle Rechnung const tableColumns = [`${loopTag}_position`, `${loopTag}_name`, `${loopTag}_value`, `${loopTag}_date`]; data[loopTag] = DemoDataGenerator.generateTableData(tableColumns); }); } const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, }); doc.render(data); const buf = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE', }); fs.writeFileSync(outputPath, buf); return { success: true, data: data, extractedTags: { simpleTags, loopTags } }; } catch (error) { console.error('Template Verarbeitung fehlgeschlagen:', error); return { success: false, error: error.message }; } } } // Routes // Hauptseite app.get('/', (req, res) => { res.send(` DOCX Template Server

DOCX Template Server

📄 Template hochladen und verarbeiten


📁 Office-Integration

Word WebDAV Templates: http://localhost:${PORT}/webdav/templates/
Word WebDAV Dokumente: http://localhost:${PORT}/webdav/documents/
${fs.existsSync(path.join(__dirname, '203_key.pem')) && fs.existsSync(path.join(__dirname, '203_cert.pem')) ? `Templates (HTTPS): https://localhost:443/webdav/templates/
Dokumente (HTTPS): https://localhost:443/webdav/documents/` : 'HTTPS verfügbar sobald SSL-Zertifikate (203_cert.pem und 203_key.pem) im Projektverzeichnis vorhanden sind'}

Office-Integration: Öffnen Sie in Word/Excel: Datei → Öffnen → Netzwerk hinzufügen → WebDAV → http://localhost:${PORT}/webdav/templates/

Direkt speichern: Word speichert automatisch auf dem Server wenn über WebDAV geöffnet.

🔧 API Endpunkte

Word-Integration: Die API liefert spezielle Links für Microsoft Word:

📋 Test Template

Test Template erstellen

Erstellt ein Beispiel-Template mit verschiedenen Tag-Typen für Tests.

`); }); // Template Upload und Verarbeitung app.post('/upload-template', upload.single('template'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'Keine Datei hochgeladen' }); } const templateName = req.file.originalname; const templatePath = path.join(templateDir, templateName); const outputName = templateName.replace('.docx', '_ausgefuellt.docx'); const outputPath = path.join(outputDir, outputName); // Template in Templates-Verzeichnis kopieren fs.copyFileSync(req.file.path, templatePath); // Template verarbeiten const result = await TemplateProcessor.processTemplate(templatePath, outputPath); // Upload-Datei löschen fs.unlinkSync(req.file.path); if (result.success) { const protocol = req.secure ? 'https' : 'http'; const host = req.get('host') || `localhost:${PORT}`; res.json({ message: 'Template erfolgreich verarbeitet', templateName: templateName, outputName: outputName, extractedTags: result.extractedTags, generatedData: result.data, fileUrls: { template: `${protocol}://${host}/webdav/templates/${templateName}`, document: `${protocol}://${host}/webdav/documents/${outputName}`, wordWebdavTemplate: `ms-word:ofe|u|${protocol}://${host}/webdav/templates/${encodeURIComponent(templateName)}`, wordWebdavDocument: `ms-word:ofe|u|${protocol}://${host}/webdav/documents/${encodeURIComponent(outputName)}`, wordDirectTemplate: `word:ofe|u|${protocol}://${host}/webdav/templates/${encodeURIComponent(templateName)}`, wordDirectDocument: `word:ofe|u|${protocol}://${host}/webdav/documents/${encodeURIComponent(outputName)}` } }); } else { res.status(500).json({ error: result.error }); } } catch (error) { console.error('Upload Fehler:', error); res.status(500).json({ error: 'Server Fehler beim Upload' }); } }); // Test Template erstellen app.get('/create-test-template', (req, res) => { const PizZip = require('pizzip'); // Minimales DOCX Template erstellen const testContent = ` Firmenname: {firma} Ansprechpartner: {vorname} {nachname} E-Mail: {email} Telefon: {telefon} Adresse: {adresse}, {plz} {stadt} Datum: {datum} Rechnungsnummer: {nummer} Positionen: {#items} - {items_name}: {items_value} EUR ({items_date}) {/items} Gesamtbetrag: {betrag} EUR Beschreibung: {beschreibung} `; try { // Minimal DOCX Struktur const zip = new PizZip(); // document.xml zip.file('word/document.xml', testContent); // [Content_Types].xml zip.file('[Content_Types].xml', ` `); // _rels/.rels zip.file('_rels/.rels', ` `); // word/_rels/document.xml.rels zip.file('word/_rels/document.xml.rels', ` `); const buffer = zip.generate({ type: 'nodebuffer' }); const testTemplatePath = path.join(templateDir, 'test_template.docx'); fs.writeFileSync(testTemplatePath, buffer); res.json({ message: 'Test Template erstellt', templatePath: 'test_template.docx', fileUrl: `http://localhost:${PORT}/webdav/templates/test_template.docx`, info: 'Sie können das Template jetzt über den Datei-Server herunterladen, bearbeiten und wieder hochladen.' }); } catch (error) { console.error('Fehler beim Erstellen des Test Templates:', error); res.status(500).json({ error: 'Fehler beim Erstellen des Test Templates' }); } }); // API Endpunkte app.get('/api/templates', (req, res) => { try { const files = fs.readdirSync(templateDir).filter(file => file.endsWith('.docx')); const protocol = req.secure ? 'https' : 'http'; const host = req.get('host') || `localhost:${PORT}`; res.json({ templates: files.map(file => ({ name: file, fileUrl: `${protocol}://${host}/webdav/templates/${file}`, wordWebdavUrl: `ms-word:ofe|u|${protocol}://${host}/webdav/templates/${encodeURIComponent(file)}`, wordDirectUrl: `word:ofe|u|${protocol}://${host}/webdav/templates/${encodeURIComponent(file)}`, downloadUrl: `${protocol}://${host}/webdav/templates/${file}`, webdavDirect: `\\\\${host.split(':')[0]}@${PORT}\\webdav\\templates\\${file}` })) }); } catch (error) { res.status(500).json({ error: 'Fehler beim Auflisten der Templates' }); } }); app.get('/api/documents', (req, res) => { try { const files = fs.readdirSync(outputDir).filter(file => file.endsWith('.docx')); const protocol = req.secure ? 'https' : 'http'; const host = req.get('host') || `localhost:${PORT}`; res.json({ documents: files.map(file => ({ name: file, fileUrl: `${protocol}://${host}/webdav/documents/${file}`, wordWebdavUrl: `ms-word:ofe|u|${protocol}://${host}/webdav/documents/${encodeURIComponent(file)}`, wordDirectUrl: `word:ofe|u|${protocol}://${host}/webdav/documents/${encodeURIComponent(file)}`, downloadUrl: `${protocol}://${host}/webdav/documents/${file}`, webdavDirect: `\\\\${host.split(':')[0]}@${PORT}\\webdav\\documents\\${file}`, createdAt: fs.statSync(path.join(outputDir, file)).mtime })) }); } catch (error) { res.status(500).json({ error: 'Fehler beim Auflisten der Dokumente' }); } }); // Template mit benutzerdefinierten Daten verarbeiten app.post('/api/process-template/:templateName', (req, res) => { try { const templateName = req.params.templateName; const templatePath = path.join(templateDir, templateName); if (!fs.existsSync(templatePath)) { return res.status(404).json({ error: 'Template nicht gefunden' }); } const outputName = templateName.replace('.docx', '_custom.docx'); const outputPath = path.join(outputDir, outputName); TemplateProcessor.processTemplate(templatePath, outputPath, req.body).then(result => { if (result.success) { res.json({ message: 'Template mit benutzerdefinierten Daten verarbeitet', outputName: outputName, fileUrl: `http://localhost:${PORT}/webdav/documents/${outputName}` }); } else { res.status(500).json({ error: result.error }); } }); } catch (error) { res.status(500).json({ error: 'Fehler beim Verarbeiten des Templates' }); } }); // SSL Konfiguration const certPath = path.join(__dirname, '203_cert.pem'); const keyPath = path.join(__dirname, '203_key.pem'); if (fs.existsSync(keyPath) && fs.existsSync(certPath)) { try { const sslOptions = { key: fs.readFileSync(keyPath), cert: fs.readFileSync(certPath) }; https.createServer(sslOptions, app).listen(443, () => { console.log('🔒 HTTPS Server läuft auf Port 443'); console.log('🌐 HTTPS Zugang: https://localhost:443'); }); } catch (error) { console.warn('⚠️ SSL-Zertifikate gefunden, aber Fehler beim Laden:', error.message); console.log('ℹ️ Server läuft nur mit HTTP'); } } else { console.log('ℹ️ SSL-Zertifikate nicht gefunden - Server läuft nur mit HTTP'); console.log('💡 Für HTTPS: Platzieren Sie 203_cert.pem und 203_key.pem in /home/OfficeServerJS/'); } // Server starten app.listen(PORT, () => { console.log(`\n🚀 DOCX Template Server gestartet!`); console.log(`📍 HTTP Server: http://localhost:${PORT}`); console.log(`📁 Templates: http://localhost:${PORT}/webdav/templates/`); console.log(`📁 Documents: http://localhost:${PORT}/webdav/documents/`); console.log(`\n💡 Tipp: Besuchen Sie http://localhost:${PORT} für die Web-Oberfläche`); // SSL-Status anzeigen const certPath = path.join(__dirname, '203_cert.pem'); const keyPath = path.join(__dirname, '203_key.pem'); if (fs.existsSync(keyPath) && fs.existsSync(certPath)) { console.log(`🔒 HTTPS auch verfügbar: https://localhost:443`); } }); module.exports = app;