🗑️ WebDAV komplett entfernt

 Bereinigungen:
- Alle WebDAV-bezogenen Code-Teile entfernt
- Dokumentation auf HTTP-Zugriff umgestellt
- Veraltete WebDAV-Backup-Dateien gelöscht
- URLs von /webdav/* auf direkte Pfade geändert

📁 Gelöschte Dateien:
- WEBDAV-INTEGRATION.md
- SCHREIBSCHUTZ-BEHOBEN.md
- server_old.js
- server_webdav_backup.js

🔄 Aktualisierte URLs:
- /webdav/templates/ → /templates/
- /webdav/documents/ → /documents/

📝 Bereinigte Dokumentation:
- README.md - WebDAV-Referenzen entfernt
- STATUS.md - URLs aktualisiert
- SSL-*.md - WebDAV-Links ersetzt
- Start-Scripts - Pfade korrigiert

🎯 Fokus jetzt auf:
- HTTP-basierte Dateifreigabe
- Management-GUI System
- Custom Tags REST-APIs
- Template-Verarbeitung ohne WebDAV-Komplexität
This commit is contained in:
OfficeServer dgsoft 2025-10-05 20:58:02 +02:00
parent 88932bfbf4
commit 83d345075b
15 changed files with 45 additions and 2172 deletions

1
.gitignore vendored
View File

@ -4,7 +4,6 @@ uploads/
.env
.DS_Store
Thumbs.db
webdav-tree.json
203_cert.pem
203_key.pem
private-key.pem

View File

@ -1,6 +1,6 @@
# DOCX Template Server
Ein Node.js-Server für die automatische Verarbeitung von DOCX-Templates mit WebDAV-ähnlicher Dateifreigabe.
Ein Node.js-Server für die automatische Verarbeitung von DOCX-Templates mit Management-GUI.
## ✨ Features
@ -25,9 +25,11 @@ npm start
## 🌐 Zugriff
- **Web-Interface:** http://localhost:80
- **Templates:** http://localhost:80/webdav/templates/
- **Dokumente:** http://localhost:80/webdav/documents/
### 🌐 **Web-Zugriff:**
- **Server:** http://localhost:80/
- **Management:** http://localhost:3000/
- **Templates:** http://localhost:80/templates/
- **Dokumente:** http://localhost:80/documents/
## 📋 Verwendung
@ -41,7 +43,8 @@ Erstellen Sie ein DOCX-Dokument mit Tags wie:
### 2. Template hochladen
- Über Web-Interface: Datei auswählen und hochladen
- Über API: `POST /upload-template`
- Über Dateifreigabe: Datei in `/webdav/templates/` kopieren
- Über Web-Upload: http://localhost:80/ → "Template hochladen"
- Direkt in Ordner: Datei in `/templates/` kopieren
### 3. Automatische Verarbeitung
Der Server erkennt automatisch alle Tags und füllt sie mit passenden Demo-Daten:
@ -125,25 +128,12 @@ Body: {
}
```
## 💾 Dateifreigabe Setup
## 🌐 HTTP-Zugriff
### Windows Explorer
1. Windows Explorer öffnen
2. Adressleiste: `\\localhost\webdav$`
3. Oder: "Netzlaufwerk verbinden" → `http://localhost:80/webdav/templates/`
### Linux/macOS
```bash
# Verzeichnis erstellen
sudo mkdir -p /mnt/docx-templates
sudo mkdir -p /mnt/docx-documents
# Mounten (davfs2 erforderlich)
sudo mount -t davfs http://localhost:80/webdav/templates/ /mnt/docx-templates
sudo mount -t davfs http://localhost:80/webdav/documents/ /mnt/docx-documents
# Oder über GUI: Dateimanager → "Mit Server verbinden" → http://localhost:80/webdav/templates/
```
### Direkte HTTP-URLs:
Templates und generierte Dokumente sind über HTTP zugänglich:
- **Templates:** http://localhost:80/templates/
- **Dokumente:** http://localhost:80/documents/
## 🔒 SSL/HTTPS Einrichtung
@ -297,15 +287,10 @@ ls -la private-key.pem certificate.pem
- Tags richtig geschrieben (`{tag}` nicht `{{tag}}`)?
- Datei-Berechtigungen prüfen
### Dateifreigabe funktioniert nicht
```bash
# WebDAV-Client installieren
# Ubuntu/Debian:
sudo apt install davfs2
# macOS:
brew install davfs2
```
### Templates nicht sichtbar
Prüfen Sie:
- Server läuft: `http://localhost:80/`
- Templates-Ordner: `http://localhost:80/templates/`
### SSL-Probleme
```bash

View File

@ -1,127 +0,0 @@
# 🔓 SCHREIBSCHUTZ-PROBLEM BEHOBEN!
## ✅ **Erweiterte WebDAV-Implementierung**
Ich habe die WebDAV-Implementierung erweitert, um das Schreibschutz-Problem zu lösen:
### 🔧 **Was wurde verbessert:**
#### **1. Erweiterte WebDAV-Properties:**
- `<D:isreadonly>F</D:isreadonly>` - Explizit NICHT schreibgeschützt
- `<O:IsReadonly>F</O:IsReadonly>` - Microsoft Office spezifisch
- `<D:executable>F</D:executable>` - Nicht ausführbar
- `<D:ishidden>F</D:ishidden>` - Nicht versteckt
#### **2. Office-kompatible HTTP-Header:**
- `Server: Microsoft-IIS/10.0` - Word erkennt als IIS-Server
- `DAV: 1, 2, ordered-collections, versioning` - Vollständige WebDAV-Unterstützung
- `Cache-Control: no-cache` - Keine Zwischenspeicherung
- `ETag` und `Last-Modified` für Versionskontrolle
#### **3. Automatische Dateiberechtigungen:**
- Dateien werden mit `chmod 666` (rw-rw-rw-) gespeichert
- Explizite Schreibrechte für alle Benutzer
#### **4. Bessere Lock-Unterstützung:**
- Sowohl exklusive als auch geteilte Locks
- Office-kompatible Lock-Token
### 📝 **Neue Word-Integration:**
#### **Schritt 1: WebDAV-Netzlaufwerk einrichten**
```
1. Windows Explorer öffnen
2. "Dieser PC" → Rechtsklick → "Netzlaufwerk verbinden"
3. Ordner: \\localhost\webdav\templates (oder \documents)
4. Port: 80
5. NICHT "Mit anderen Anmeldeinformationen" ankreuzen
```
#### **Schritt 2: In Word öffnen**
```
1. Word öffnen
2. Datei → Öffnen
3. Das neue Netzlaufwerk auswählen
4. Template öffnen
```
#### **Schritt 3: Test der Schreibrechte**
```
1. Text bearbeiten
2. Strg+S drücken
3. Sollte jetzt DIREKT auf dem Server speichern!
```
### 🚨 **Troubleshooting:**
#### **Falls immer noch schreibgeschützt:**
**Option 1: Dateiberechtigungen prüfen**
```bash
cd /home/OfficeServerJS/templates
ls -la *.docx
# Sollte zeigen: -rw-rw-rw-
```
**Option 2: Alternative WebDAV-URL**
```
Statt: http://localhost:80/webdav/templates/
Verwenden: http://localhost/webdav/templates/
Oder: http://127.0.0.1:80/webdav/templates/
```
**Option 3: Windows WebDAV-Client konfigurieren**
```cmd
# Als Administrator ausführen:
net stop webclient
net start webclient
# WebDAV Registry-Einstellungen:
reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters /v BasicAuthLevel /t REG_DWORD /d 2 /f
```
**Option 4: Office Trust Center**
```
1. Word → Datei → Optionen
2. Trust Center → Trust Center-Einstellungen
3. Geschützte Ansicht → Alle Häkchen entfernen
4. Datenschutz-Optionen → "Dateien aus dem Internet..." deaktivieren
```
### 🔍 **Live-Monitoring:**
Der Server zeigt jetzt Live-Logs:
```
📖 Word öffnet Template: rechnung_template.docx
📝 Word speichert Template: rechnung_template.docx
✅ Template gespeichert: rechnung_template.docx
```
### 🎯 **Test-Verfahren:**
1. **Server-Logs beobachten** beim Öffnen/Speichern
2. **Datei-Timestamps prüfen** nach dem Speichern
3. **Browser-Zugriff testen**: http://localhost:80/webdav/templates/
### ⚡ **Sofort-Test:**
```bash
# 1. Datei über WebDAV hochladen
curl -X PUT --data-binary @test.docx http://localhost:80/webdav/templates/test.docx
# 2. Berechtigung prüfen
ls -la /home/OfficeServerJS/templates/test.docx
# 3. WebDAV-Properties abfragen
curl -X PROPFIND -H "Depth: 1" http://localhost:80/webdav/templates/test.docx
```
## 🎉 **Ergebnis:**
**Erweiterte WebDAV-Properties** für Office-Kompatibilität
**Explizite Schreibrechte** in XML-Response
**Automatische Dateiberechtigungen** beim Speichern
**Office-optimierte HTTP-Header**
**Live-Monitoring** für Debugging
**Word sollte jetzt die Dateien als beschreibbar erkennen und direkt auf dem Server speichern! 🔓✍️**

View File

@ -41,13 +41,13 @@ cd /home/OfficeServerJS
### HTTP (immer verfügbar):
- **Web-Interface:** http://localhost:80
- **Templates:** http://localhost:80/webdav/templates/
- **Dokumente:** http://localhost:80/webdav/documents/
- **Templates:** http://localhost:80/templates/
- **Dokumente:** http://localhost:80/documents/
### HTTPS (nach SSL-Aktivierung):
- **Web-Interface:** https://localhost:443
- **Templates:** https://localhost:443/webdav/templates/
- **Dokumente:** https://localhost:443/webdav/documents/
- **Templates:** https://localhost:443/templates/
- **Dokumente:** https://localhost:443/documents/
## 🔧 Zertifikat-Validierung

View File

@ -15,14 +15,14 @@ Ihr DOCX Template Server läuft jetzt mit **vollständiger SSL-Verschlüsselung*
#### HTTP (Port 80):
- **Web-Interface:** http://localhost:80
- **Templates:** http://localhost:80/webdav/templates/
- **Dokumente:** http://localhost:80/webdav/documents/
- **Templates:** http://localhost:80/templates/
- **Dokumente:** http://localhost:80/documents/
- **API:** http://localhost:80/api/
#### HTTPS (Port 443) - **NEU AKTIV**:
- **Web-Interface:** https://localhost:443
- **Templates:** https://localhost:443/webdav/templates/
- **Dokumente:** https://localhost:443/webdav/documents/
- **Templates:** https://localhost:443/templates/
- **Dokumente:** https://localhost:443/documents/
- **API:** https://localhost:443/api/
### 🚀 **Server-Features:**
@ -31,7 +31,7 @@ Ihr DOCX Template Server läuft jetzt mit **vollständiger SSL-Verschlüsselung*
- ✅ **DOCX-Template-Verarbeitung** mit automatischer Tag-Erkennung
- ✅ **Demo-Daten-Generierung** für alle Tag-Typen
- ✅ **Tabellen-Unterstützung** für Listen und Wiederholungen
- ✅ **WebDAV-ähnliche Dateifreigabe** ohne Authentifizierung
- ✅ **HTTP-Dateifreigabe** ohne Authentifizierung
- ✅ **Vollständige API** für programmatischen Zugriff
### 📋 **Einsatz in der Produktion:**
@ -103,7 +103,7 @@ curl -I -k https://localhost:443
**docxtemplater-Integration** mit intelligenter Tag-Erkennung
**Automatische Demo-Daten-Generierung**
**Tabellen- und Listen-Unterstützung**
**WebDAV-ähnliche Dateifreigabe** ohne Authentifizierung
**HTTP-Dateifreigabe** ohne Authentifizierung
**Produktionsbereit** mit vollständiger Dokumentation
**Ihr DOCX Template Server ist vollständig einsatzbereit und SSL-gesichert! 🔒✨**

View File

@ -8,14 +8,14 @@
- ✅ **Intelligente Tag-Erkennung** - erkennt automatisch alle `{tags}` im Template
- ✅ **Demo-Daten-Generierung** - füllt Tags mit realistischen Testdaten
- ✅ **Tabellen-Unterstützung** - `{#items}...{/items}` für Listen/Tabellen
- ✅ **WebDAV-ähnliche Dateifreigabe** - Templates und Dokumente ohne Auth zugänglich
- ✅ **HTTP-Dateifreigabe** - Templates und Dokumente über HTTP zugänglich
- ✅ **Web-Interface** - Benutzerfreundliche Oberfläche
- ✅ **SSL-Vorbereitung** - Ready für HTTPS mit Zertifikaten
### 📁 Verfügbare Endpoints
- ✅ `http://localhost:80` - Web-Interface
- ✅ `http://localhost:80/webdav/templates/` - Template-Verzeichnis
- ✅ `http://localhost:80/webdav/documents/` - Dokument-Verzeichnis
- ✅ `http://localhost:80/templates/` - Template-Verzeichnis
- ✅ `http://localhost:80/documents/` - Dokument-Verzeichnis
- ✅ `http://localhost:80/api/templates` - Template-API
- ✅ `http://localhost:80/api/documents` - Dokument-API
@ -48,8 +48,8 @@ Der Server erkennt automatisch und befüllt:
Der Server ist aktiv und einsatzbereit:
- 🌐 Web-Interface: http://localhost:80
- 📁 Templates: http://localhost:80/webdav/templates/
- 📁 Dokumente: http://localhost:80/webdav/documents/
- 📁 Templates: http://localhost:80/templates/
- 📁 Dokumente: http://localhost:80/documents/
## 🎯 Nächste Schritte:
@ -78,10 +78,10 @@ Der Server ist aktiv und einsatzbereit:
4. Fertiges Dokument herunterladen
### Upload via Dateifreigabe:
1. Template in `http://localhost:80/webdav/templates/` kopieren
1. Template in `http://localhost:80/templates/` hochladen
2. Server erkennt Template automatisch
3. Über API verarbeiten lassen
4. Ergebnis aus `http://localhost:80/webdav/documents/` abholen
4. Ergebnis aus `http://localhost:80/documents/` abholen
### API-Verwendung:
```bash
@ -101,7 +101,7 @@ curl -X POST -H "Content-Type: application/json" \
**Automatische Tag-Erkennung**
**Demo-Daten-Generierung**
**Tabellen-Unterstützung**
**WebDAV-ähnliche Dateifreigabe** ohne Auth
**HTTP-Dateifreigabe** ohne Auth
**SSL-Vorbereitung**
**Vollständige Dokumentation**

View File

@ -38,12 +38,12 @@
### 🌐 **Zugriff auf Templates und Dokumente:**
**HTTP:**
- Templates: http://localhost:80/webdav/templates/
- Dokumente: http://localhost:80/webdav/documents/
- Templates: http://localhost:80/templates/
- Dokumente: http://localhost:80/documents/
**HTTPS:**
- Templates: https://localhost:443/webdav/templates/
- Dokumente: https://localhost:443/webdav/documents/
- Templates: https://localhost:443/templates/
- Dokumente: https://localhost:443/documents/
### 🧪 **Demo-Daten werden automatisch generiert:**
- **Position:** 1, 2, 3, 4, 5... (fortlaufend)

View File

@ -1,106 +0,0 @@
# 🔗 WORD-WEBDAV-INTEGRATION EINGERICHTET!
## ✅ **Server mit echter WebDAV-Unterstützung**
Ihr Server unterstützt jetzt **echte WebDAV-Funktionen** für direktes Speichern in Word/Office:
### 🌐 **WebDAV-Endpunkte:**
- **Templates:** http://localhost:80/webdav/templates/
- **Dokumente:** http://localhost:80/webdav/documents/
- **HTTPS Templates:** https://localhost:443/webdav/templates/
- **HTTPS Dokumente:** https://localhost:443/webdav/documents/
## 📝 **Word-Integration Setup:**
### **Methode 1: Word → Datei → Öffnen → Netzwerk hinzufügen**
1. Öffnen Sie Microsoft Word
2. Gehen Sie zu **Datei** → **Öffnen**
3. Klicken Sie auf **Netzwerk hinzufügen**
4. Wählen Sie **WebDAV**
5. Geben Sie ein: `http://localhost:80/webdav/templates/`
6. **Kein Benutzername/Passwort erforderlich** (einfach OK klicken)
### **Methode 2: Windows Explorer → Netzlaufwerk**
1. Öffnen Sie Windows Explorer
2. Rechtsklick auf **Dieser PC**
3. **Netzlaufwerk verbinden**
4. Als Ordner eingeben: `http://localhost:80/webdav/templates/`
5. **"Anmeldung mit anderen Anmeldeinformationen"** NICHT ankreuzen
### **Methode 3: Direkte URL in Word**
- Templates: `\\localhost@80\webdav\templates\`
- Dokumente: `\\localhost@80\webdav\documents\`
## 🔧 **Was jetzt funktioniert:**
### ✅ **WebDAV-Funktionen implementiert:**
- **PROPFIND** - Dateilisten für Office
- **GET/PUT** - Dateien öffnen und speichern
- **LOCK/UNLOCK** - Office-Sperrmechanismus
- **DAV-Header** - Word erkennt WebDAV-Server
### ✅ **Word-Integration:**
- **Direktes Öffnen** von Templates über WebDAV
- **Automatisches Speichern** auf dem Server (nicht lokal!)
- **Versionskontrolle** durch Server-basierte Speicherung
- **Gleichzeitiges Arbeiten** mit Locks
### ✅ **API erweitert:**
```json
{
"templates": [
{
"name": "rechnung_template.docx",
"fileUrl": "http://localhost:80/webdav/templates/rechnung_template.docx",
"wordWebdavUrl": "ms-word:ofe|u|http://localhost:80/webdav/templates/rechnung_template.docx",
"wordDirectUrl": "word:ofe|u|http://localhost:80/webdav/templates/rechnung_template.docx",
"downloadUrl": "http://localhost:80/webdav/templates/rechnung_template.docx",
"webdavDirect": "\\\\localhost@80\\webdav\\templates\\rechnung_template.docx"
}
]
}
```
## 🎯 **So funktioniert das Speichern:**
### **Vorher (Problem):**
- Word öffnet Datei → Bearbeiten → **Speichern geht nur lokal**
### **Jetzt (Lösung):**
- Word öffnet über WebDAV → Bearbeiten → **Speichern geht direkt auf Server!**
## 🔍 **Test der WebDAV-Funktionen:**
### Server-Status prüfen:
```bash
curl -X PROPFIND -H "Depth: 1" http://localhost:80/webdav/templates/
```
### Datei hochladen via WebDAV:
```bash
curl -X PUT --data-binary @meine_datei.docx http://localhost:80/webdav/templates/meine_datei.docx
```
### API-Test:
```bash
curl http://localhost:80/api/templates
```
## 📋 **Anleitung für Benutzer:**
1. **Word öffnen**
2. **Datei → Öffnen → Netzwerk hinzufügen → WebDAV**
3. **URL eingeben:** `http://localhost:80/webdav/templates/`
4. **Template auswählen und öffnen**
5. **Bearbeiten**
6. **Strg+S drücken** → **Speichert automatisch auf dem Server!**
## 🚀 **Ergebnis:**
**Echte WebDAV-Integration** für Office
**Direktes Speichern** auf dem Server
**Keine lokalen Kopien** mehr nötig
**Professioneller Workflow** für Teams
**Zentrale Datenverwaltung** auf dem Server
**Word speichert jetzt automatisch auf dem Server, wenn über WebDAV geöffnet! 🎉**

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,939 +0,0 @@
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 = `<?xml version="1.0" encoding="utf-8"?>
<D:multistatus 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">
${items.map(item => `
<D:response>
<D:href>${item.href}</D:href>
<D:propstat>
<D:prop>
<D:displayname>${path.basename(item.href)}</D:displayname>
<D:getlastmodified>${item.lastModified}</D:getlastmodified>
<D:getcontentlength>${item.size}</D:getcontentlength>
<D:getcontenttype>${item.contentType}</D:getcontenttype>
<D:resourcetype>${item.isDirectory ? '<D:collection/>' : ''}</D:resourcetype>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:getetag>"${Date.now()}-${item.size}"</D:getetag>
<D:creationdate>${new Date(item.lastModified).toISOString()}</D:creationdate>
<D:iscollection>${item.isDirectory ? 'true' : 'false'}</D:iscollection>
<D:executable>F</D:executable>
<D:ishidden>F</D:ishidden>
<D:isreadonly>F</D:isreadonly>
<O:IsReadonly>F</O:IsReadonly>
<Z:Win32CreationTime>${new Date(item.lastModified).toISOString()}</Z:Win32CreationTime>
<Z:Win32LastAccessTime>${new Date().toISOString()}</Z:Win32LastAccessTime>
<Z:Win32LastModifiedTime>${new Date(item.lastModified).toISOString()}</Z:Win32LastModifiedTime>
<Z:Win32FileAttributes>00000020</Z:Win32FileAttributes>
<S:modifiedby>System Account</S:modifiedby>
<S:checkedoutby></S:checkedoutby>
<S:IsCheckedoutToLocal>0</S:IsCheckedoutToLocal>
<S:CheckoutExpires></S:CheckoutExpires>
<S:CanCheckOut>1</S:CanCheckOut>
<S:ContentTypeId>0x0101</S:ContentTypeId>
<S:DocumentWorkflowStatus>0</S:DocumentWorkflowStatus>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>`).join('')}
</D:multistatus>`;
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(`<EFBFBD> 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 = `<?xml version="1.0" encoding="utf-8"?>
<D:prop xmlns:D="DAV:">
<D:lockdiscovery>
<D:activelock>
<D:locktype><D:write/></D:locktype>
<D:lockscope><D:exclusive/></D:lockscope>
<D:depth>0</D:depth>
<D:timeout>Second-604800</D:timeout>
<D:locktoken><D:href>${lockToken}</D:href></D:locktoken>
</D:activelock>
</D:lockdiscovery>
</D:prop>`;
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 = `<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>${req.originalUrl}</D:href>
<D:propstat>
<D:status>HTTP/1.1 200 OK</D:status>
<D:prop>
<D:resourcetype><D:collection/></D:resourcetype>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
</D:prop>
</D:propstat>
</D:response>
</D:multistatus>`;
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(`<!-- FrontPage Configuration Information
FP_VER:15.0.0.0000
FP_SCHEMA_VER:15.0.0.0000
-->`);
});
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 = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetListCollectionResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<GetListCollectionResult>
<Lists>
<List DocTemplateUrl="" DefaultViewUrl="/templates" ID="{${crypto.randomUUID()}}" Title="Templates" Description="Document Templates" ImageUrl="/_layouts/images/itdl.gif" Name="{${crypto.randomUUID()}}" BaseType="1" ServerTemplate="101" Created="2025-10-02T19:00:00Z" Modified="2025-10-02T19:00:00Z" LastDeleted="1900-01-01T00:00:00Z" Version="1" Direction="none" ThumbnailSize="0" WebImageWidth="0" WebImageHeight="0" Flags="0" ItemCount="3" AnonymousPermMask="0" RootFolder="/webdav/templates" ReadSecurity="1" WriteSecurity="1" Author="1" InheritedSecurity="true" AllowMultiResponses="false" AllowDeletion="true" AllowAnonymousAccess="false" EnableAttachments="true" EnableModeration="false" EnableVersioning="false" Hidden="false" MultipleDataList="false" Ordered="false" ShowUser="true" EnablePeopleSelector="false" EnableResourceSelector="false" EnableMinorVersion="false" RequireCheckout="false" />
</Lists>
</GetListCollectionResult>
</GetListCollectionResponse>
</soap:Body>
</soap:Envelope>`;
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(`<?xml version="1.0" encoding="utf-8"?>
<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
<s:Schema id="RowsetSchema">
<s:ElementType name="row" content="eltOnly" rs:updatable="true">
<s:AttributeType name="ows_Title" rs:name="Title" rs:number="1"/>
<s:AttributeType name="ows_Modified" rs:name="Modified" rs:number="2"/>
<s:AttributeType name="ows_Editor" rs:name="Editor" rs:number="3"/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row ows_Title="Templates" ows_Modified="2025-10-02T19:00:00Z" ows_Editor="System Account"/>
</rs:data>
</xml>`);
} 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(`
<!DOCTYPE html>
<html>
<head>
<title>DOCX Template Server</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; }
.section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
.button { background: #007cba; color: white; padding: 10px 20px; text-decoration: none; border-radius: 3px; display: inline-block; margin: 5px; }
.button:hover { background: #005a87; }
input[type="file"] { margin: 10px 0; }
.info { background: #f0f8ff; padding: 10px; border-left: 4px solid #007cba; margin: 10px 0; }
</style>
</head>
<body>
<div class="container">
<h1>DOCX Template Server</h1>
<div class="section">
<h2>📄 Template hochladen und verarbeiten</h2>
<form action="/upload-template" method="post" enctype="multipart/form-data">
<input type="file" name="template" accept=".docx" required>
<br>
<input type="submit" value="Template hochladen und verarbeiten" class="button">
</form>
</div>
<div class="section">
<h2>📁 Office-Integration</h2>
<div class="info">
<strong>Word WebDAV Templates:</strong> <a href="/webdav/templates/" target="_blank">http://localhost:${PORT}/webdav/templates/</a><br>
<strong>Word WebDAV Dokumente:</strong> <a href="/webdav/documents/" target="_blank">http://localhost:${PORT}/webdav/documents/</a><br>
${fs.existsSync(path.join(__dirname, '203_key.pem')) && fs.existsSync(path.join(__dirname, '203_cert.pem')) ?
`<strong>Templates (HTTPS):</strong> <a href="https://localhost:443/webdav/templates/" target="_blank">https://localhost:443/webdav/templates/</a><br>
<strong>Dokumente (HTTPS):</strong> <a href="https://localhost:443/webdav/documents/" target="_blank">https://localhost:443/webdav/documents/</a>` :
'<em>HTTPS verfügbar sobald SSL-Zertifikate (203_cert.pem und 203_key.pem) im Projektverzeichnis vorhanden sind</em>'}
</div>
<p><strong>Office-Integration:</strong> Öffnen Sie in Word/Excel: Datei Öffnen Netzwerk hinzufügen WebDAV http://localhost:${PORT}/webdav/templates/</p>
<p><strong>Direkt speichern:</strong> Word speichert automatisch auf dem Server wenn über WebDAV geöffnet.</p>
</div>
<div class="section">
<h2>🔧 API Endpunkte</h2>
<ul>
<li><a href="/api/templates" class="button">Templates auflisten</a></li>
<li><a href="/api/documents" class="button">Dokumente auflisten</a></li>
</ul>
<p><strong>Word-Integration:</strong> Die API liefert spezielle Links für Microsoft Word:</p>
<ul>
<li><code>wordWebdavUrl</code> - Direkt in Word öffnen (ms-word: Protocol)</li>
<li><code>wordDirectUrl</code> - Alternative Word-Integration</li>
<li><code>webdavDirect</code> - Windows UNC-Pfad für Netzlaufwerk</li>
</ul>
</div>
<div class="section">
<h2>📋 Test Template</h2>
<a href="/create-test-template" class="button">Test Template erstellen</a>
<p>Erstellt ein Beispiel-Template mit verschiedenen Tag-Typen für Tests.</p>
</div>
</div>
</body>
</html>
`);
});
// 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 = `
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p><w:r><w:t>Firmenname: {firma}</w:t></w:r></w:p>
<w:p><w:r><w:t>Ansprechpartner: {vorname} {nachname}</w:t></w:r></w:p>
<w:p><w:r><w:t>E-Mail: {email}</w:t></w:r></w:p>
<w:p><w:r><w:t>Telefon: {telefon}</w:t></w:r></w:p>
<w:p><w:r><w:t>Adresse: {adresse}, {plz} {stadt}</w:t></w:r></w:p>
<w:p><w:r><w:t>Datum: {datum}</w:t></w:r></w:p>
<w:p><w:r><w:t>Rechnungsnummer: {nummer}</w:t></w:r></w:p>
<w:p><w:r><w:t>Positionen:</w:t></w:r></w:p>
<w:p><w:r><w:t>{#items}</w:t></w:r></w:p>
<w:p><w:r><w:t>- {items_name}: {items_value} EUR ({items_date})</w:t></w:r></w:p>
<w:p><w:r><w:t>{/items}</w:t></w:r></w:p>
<w:p><w:r><w:t>Gesamtbetrag: {betrag} EUR</w:t></w:r></w:p>
<w:p><w:r><w:t>Beschreibung:</w:t></w:r></w:p>
<w:p><w:r><w:t>{beschreibung}</w:t></w:r></w:p>
</w:body>
</w:document>
`;
try {
// Minimal DOCX Struktur
const zip = new PizZip();
// document.xml
zip.file('word/document.xml', testContent);
// [Content_Types].xml
zip.file('[Content_Types].xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`);
// _rels/.rels
zip.file('_rels/.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// word/_rels/document.xml.rels
zip.file('word/_rels/document.xml.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
</Relationships>`);
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;

View File

@ -1,939 +0,0 @@
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 = `<?xml version="1.0" encoding="utf-8"?>
<D:multistatus 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">
${items.map(item => `
<D:response>
<D:href>${item.href}</D:href>
<D:propstat>
<D:prop>
<D:displayname>${path.basename(item.href)}</D:displayname>
<D:getlastmodified>${item.lastModified}</D:getlastmodified>
<D:getcontentlength>${item.size}</D:getcontentlength>
<D:getcontenttype>${item.contentType}</D:getcontenttype>
<D:resourcetype>${item.isDirectory ? '<D:collection/>' : ''}</D:resourcetype>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:getetag>"${Date.now()}-${item.size}"</D:getetag>
<D:creationdate>${new Date(item.lastModified).toISOString()}</D:creationdate>
<D:iscollection>${item.isDirectory ? 'true' : 'false'}</D:iscollection>
<D:executable>F</D:executable>
<D:ishidden>F</D:ishidden>
<D:isreadonly>F</D:isreadonly>
<O:IsReadonly>F</O:IsReadonly>
<Z:Win32CreationTime>${new Date(item.lastModified).toISOString()}</Z:Win32CreationTime>
<Z:Win32LastAccessTime>${new Date().toISOString()}</Z:Win32LastAccessTime>
<Z:Win32LastModifiedTime>${new Date(item.lastModified).toISOString()}</Z:Win32LastModifiedTime>
<Z:Win32FileAttributes>00000020</Z:Win32FileAttributes>
<S:modifiedby>System Account</S:modifiedby>
<S:checkedoutby></S:checkedoutby>
<S:IsCheckedoutToLocal>0</S:IsCheckedoutToLocal>
<S:CheckoutExpires></S:CheckoutExpires>
<S:CanCheckOut>1</S:CanCheckOut>
<S:ContentTypeId>0x0101</S:ContentTypeId>
<S:DocumentWorkflowStatus>0</S:DocumentWorkflowStatus>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>`).join('')}
</D:multistatus>`;
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(`<EFBFBD> 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 = `<?xml version="1.0" encoding="utf-8"?>
<D:prop xmlns:D="DAV:">
<D:lockdiscovery>
<D:activelock>
<D:locktype><D:write/></D:locktype>
<D:lockscope><D:exclusive/></D:lockscope>
<D:depth>0</D:depth>
<D:timeout>Second-604800</D:timeout>
<D:locktoken><D:href>${lockToken}</D:href></D:locktoken>
</D:activelock>
</D:lockdiscovery>
</D:prop>`;
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 = `<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>${req.originalUrl}</D:href>
<D:propstat>
<D:status>HTTP/1.1 200 OK</D:status>
<D:prop>
<D:resourcetype><D:collection/></D:resourcetype>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
</D:prop>
</D:propstat>
</D:response>
</D:multistatus>`;
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(`<!-- FrontPage Configuration Information
FP_VER:15.0.0.0000
FP_SCHEMA_VER:15.0.0.0000
-->`);
});
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 = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetListCollectionResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<GetListCollectionResult>
<Lists>
<List DocTemplateUrl="" DefaultViewUrl="/templates" ID="{${crypto.randomUUID()}}" Title="Templates" Description="Document Templates" ImageUrl="/_layouts/images/itdl.gif" Name="{${crypto.randomUUID()}}" BaseType="1" ServerTemplate="101" Created="2025-10-02T19:00:00Z" Modified="2025-10-02T19:00:00Z" LastDeleted="1900-01-01T00:00:00Z" Version="1" Direction="none" ThumbnailSize="0" WebImageWidth="0" WebImageHeight="0" Flags="0" ItemCount="3" AnonymousPermMask="0" RootFolder="/webdav/templates" ReadSecurity="1" WriteSecurity="1" Author="1" InheritedSecurity="true" AllowMultiResponses="false" AllowDeletion="true" AllowAnonymousAccess="false" EnableAttachments="true" EnableModeration="false" EnableVersioning="false" Hidden="false" MultipleDataList="false" Ordered="false" ShowUser="true" EnablePeopleSelector="false" EnableResourceSelector="false" EnableMinorVersion="false" RequireCheckout="false" />
</Lists>
</GetListCollectionResult>
</GetListCollectionResponse>
</soap:Body>
</soap:Envelope>`;
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(`<?xml version="1.0" encoding="utf-8"?>
<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
<s:Schema id="RowsetSchema">
<s:ElementType name="row" content="eltOnly" rs:updatable="true">
<s:AttributeType name="ows_Title" rs:name="Title" rs:number="1"/>
<s:AttributeType name="ows_Modified" rs:name="Modified" rs:number="2"/>
<s:AttributeType name="ows_Editor" rs:name="Editor" rs:number="3"/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row ows_Title="Templates" ows_Modified="2025-10-02T19:00:00Z" ows_Editor="System Account"/>
</rs:data>
</xml>`);
} 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(`
<!DOCTYPE html>
<html>
<head>
<title>DOCX Template Server</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; }
.section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
.button { background: #007cba; color: white; padding: 10px 20px; text-decoration: none; border-radius: 3px; display: inline-block; margin: 5px; }
.button:hover { background: #005a87; }
input[type="file"] { margin: 10px 0; }
.info { background: #f0f8ff; padding: 10px; border-left: 4px solid #007cba; margin: 10px 0; }
</style>
</head>
<body>
<div class="container">
<h1>DOCX Template Server</h1>
<div class="section">
<h2>📄 Template hochladen und verarbeiten</h2>
<form action="/upload-template" method="post" enctype="multipart/form-data">
<input type="file" name="template" accept=".docx" required>
<br>
<input type="submit" value="Template hochladen und verarbeiten" class="button">
</form>
</div>
<div class="section">
<h2>📁 Office-Integration</h2>
<div class="info">
<strong>Word WebDAV Templates:</strong> <a href="/webdav/templates/" target="_blank">http://localhost:${PORT}/webdav/templates/</a><br>
<strong>Word WebDAV Dokumente:</strong> <a href="/webdav/documents/" target="_blank">http://localhost:${PORT}/webdav/documents/</a><br>
${fs.existsSync(path.join(__dirname, '203_key.pem')) && fs.existsSync(path.join(__dirname, '203_cert.pem')) ?
`<strong>Templates (HTTPS):</strong> <a href="https://localhost:443/webdav/templates/" target="_blank">https://localhost:443/webdav/templates/</a><br>
<strong>Dokumente (HTTPS):</strong> <a href="https://localhost:443/webdav/documents/" target="_blank">https://localhost:443/webdav/documents/</a>` :
'<em>HTTPS verfügbar sobald SSL-Zertifikate (203_cert.pem und 203_key.pem) im Projektverzeichnis vorhanden sind</em>'}
</div>
<p><strong>Office-Integration:</strong> Öffnen Sie in Word/Excel: Datei Öffnen Netzwerk hinzufügen WebDAV http://localhost:${PORT}/webdav/templates/</p>
<p><strong>Direkt speichern:</strong> Word speichert automatisch auf dem Server wenn über WebDAV geöffnet.</p>
</div>
<div class="section">
<h2>🔧 API Endpunkte</h2>
<ul>
<li><a href="/api/templates" class="button">Templates auflisten</a></li>
<li><a href="/api/documents" class="button">Dokumente auflisten</a></li>
</ul>
<p><strong>Word-Integration:</strong> Die API liefert spezielle Links für Microsoft Word:</p>
<ul>
<li><code>wordWebdavUrl</code> - Direkt in Word öffnen (ms-word: Protocol)</li>
<li><code>wordDirectUrl</code> - Alternative Word-Integration</li>
<li><code>webdavDirect</code> - Windows UNC-Pfad für Netzlaufwerk</li>
</ul>
</div>
<div class="section">
<h2>📋 Test Template</h2>
<a href="/create-test-template" class="button">Test Template erstellen</a>
<p>Erstellt ein Beispiel-Template mit verschiedenen Tag-Typen für Tests.</p>
</div>
</div>
</body>
</html>
`);
});
// 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 = `
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p><w:r><w:t>Firmenname: {firma}</w:t></w:r></w:p>
<w:p><w:r><w:t>Ansprechpartner: {vorname} {nachname}</w:t></w:r></w:p>
<w:p><w:r><w:t>E-Mail: {email}</w:t></w:r></w:p>
<w:p><w:r><w:t>Telefon: {telefon}</w:t></w:r></w:p>
<w:p><w:r><w:t>Adresse: {adresse}, {plz} {stadt}</w:t></w:r></w:p>
<w:p><w:r><w:t>Datum: {datum}</w:t></w:r></w:p>
<w:p><w:r><w:t>Rechnungsnummer: {nummer}</w:t></w:r></w:p>
<w:p><w:r><w:t>Positionen:</w:t></w:r></w:p>
<w:p><w:r><w:t>{#items}</w:t></w:r></w:p>
<w:p><w:r><w:t>- {items_name}: {items_value} EUR ({items_date})</w:t></w:r></w:p>
<w:p><w:r><w:t>{/items}</w:t></w:r></w:p>
<w:p><w:r><w:t>Gesamtbetrag: {betrag} EUR</w:t></w:r></w:p>
<w:p><w:r><w:t>Beschreibung:</w:t></w:r></w:p>
<w:p><w:r><w:t>{beschreibung}</w:t></w:r></w:p>
</w:body>
</w:document>
`;
try {
// Minimal DOCX Struktur
const zip = new PizZip();
// document.xml
zip.file('word/document.xml', testContent);
// [Content_Types].xml
zip.file('[Content_Types].xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`);
// _rels/.rels
zip.file('_rels/.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// word/_rels/document.xml.rels
zip.file('word/_rels/document.xml.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
</Relationships>`);
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;

View File

@ -50,10 +50,10 @@ echo " HTTP: http://localhost:80"
echo " HTTPS: https://localhost:443"
echo ""
echo "📁 Dateifreigabe:"
echo " HTTP Templates: http://localhost:80/webdav/templates/"
echo " HTTPS Templates: https://localhost:443/webdav/templates/"
echo " HTTP Documents: http://localhost:80/webdav/documents/"
echo " HTTPS Documents: https://localhost:443/webdav/documents/"
echo " HTTP Templates: http://localhost:80/templates/"
echo " HTTPS Templates: https://localhost:443/templates/"
echo " HTTP Documents: http://localhost:80/documents/"
echo " HTTPS Documents: https://localhost:443/documents/"
echo ""
# Server mit SSL starten

View File

@ -28,8 +28,8 @@ fi
echo "🌟 Starte den DOCX Template Server..."
echo ""
echo "📍 Web-Oberfläche: http://localhost:80"
echo "📁 Templates: http://localhost:80/webdav/templates/"
echo "📁 Dokumente: http://localhost:80/webdav/documents/"
echo "📁 Templates: http://localhost:80/templates/"
echo "📁 Dokumente: http://localhost:80/documents/"
echo ""
echo "💡 Drücken Sie Ctrl+C zum Beenden"
echo ""