mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-11 17:23:37 +01:00
feat: translate all pages, UI messages and docs and fix SEO redirects and indexing
This commit is contained in:
1 parent
5e019de5ae
commit
4edb1718cc
3344 files changed
+415643
-4759
No files matched your search
@@ -0,0 +1,275 @@
|
||||
# Mit Apache bereitstellen
|
||||
|
||||
Hosten Sie BentoPDF mit dem Apache HTTP Server.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Apache 2.4+
|
||||
- mod_rewrite aktiviert
|
||||
- SSL-Zertifikat (empfohlen)
|
||||
|
||||
## Schritt 1: Projekt bauen
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Um das Branding anzupassen, setzen Sie vor dem Build Umgebungsvariablen:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Schritt 2: Dateien kopieren
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Schritt 3: Apache-Konfiguration
|
||||
|
||||
Erstellen Sie `/etc/apache2/sites-available/bentopdf.conf`:
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName your-domain.com
|
||||
Redirect permanent / https://your-domain.com/
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName your-domain.com
|
||||
DocumentRoot /var/www/bentopdf
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
|
||||
|
||||
<Directory /var/www/bentopdf>
|
||||
Options -Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
# WASM MIME type
|
||||
AddType application/wasm .wasm
|
||||
|
||||
# Prevent double-compression of pre-compressed files
|
||||
SetEnvIfNoCase Request_URI "\.gz$" no-gzip
|
||||
|
||||
# Compression
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json application/wasm
|
||||
</IfModule>
|
||||
|
||||
# Cache headers
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType application/javascript "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 year"
|
||||
ExpiresByType application/wasm "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
# Required headers for SharedArrayBuffer (LibreOffice WASM)
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Resource-Policy "cross-origin"
|
||||
|
||||
# Security headers
|
||||
Header always set X-Frame-Options "SAMEORIGIN"
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
|
||||
# Pre-compressed LibreOffice WASM files
|
||||
<FilesMatch "soffice\.wasm\.gz$">
|
||||
ForceType application/wasm
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
<FilesMatch "soffice\.data\.gz$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## Schritt 4: .htaccess für das Routing
|
||||
|
||||
Erstellen Sie `/var/www/bentopdf/.htaccess`:
|
||||
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# Existing files/dirs - serve directly
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# ============================================
|
||||
# LANGUAGE ROUTES
|
||||
# ============================================
|
||||
# Supported languages: de, es, zh, zh-TW, vi, it, id, tr, fr, pt
|
||||
# English has no prefix - served from root
|
||||
|
||||
# English prefix redirects to root
|
||||
RewriteRule ^en/?$ / [R=301,L]
|
||||
RewriteRule ^en/(.+)$ /$1 [R=301,L]
|
||||
|
||||
# Language prefix root (e.g., /de/ -> /de/index.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /$1/index.html [L]
|
||||
|
||||
# Language prefix with path (e.g., /de/merge-pdf -> /de/merge-pdf.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /$1/$2.html [L]
|
||||
|
||||
# ============================================
|
||||
# ADD .HTML EXTENSION (ROOT LEVEL)
|
||||
# ============================================
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
|
||||
ErrorDocument 404 /404.html
|
||||
```
|
||||
|
||||
## Schritt 5: Erforderliche Module aktivieren
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
sudo a2enmod ssl
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod expires
|
||||
sudo a2enmod deflate
|
||||
```
|
||||
|
||||
## Schritt 6: Die Site aktivieren
|
||||
|
||||
```bash
|
||||
sudo a2ensite bentopdf.conf
|
||||
sudo apache2ctl configtest
|
||||
sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
## Bereitstellung in einem Unterverzeichnis
|
||||
|
||||
So hosten Sie unter `/pdf/`:
|
||||
|
||||
1. Mit Basis-URL bauen:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
2. `.htaccess` anpassen:
|
||||
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteBase /pdf/
|
||||
|
||||
# Existing files/dirs - serve directly
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# Language routes
|
||||
RewriteRule ^en/?$ /pdf/ [R=301,L]
|
||||
RewriteRule ^en/(.+)$ /pdf/$1 [R=301,L]
|
||||
|
||||
RewriteCond %{DOCUMENT_ROOT}/pdf/$1/index.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /pdf/$1/index.html [L]
|
||||
|
||||
RewriteCond %{DOCUMENT_ROOT}/pdf/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /pdf/$1/$2.html [L]
|
||||
|
||||
# Root level .html extension
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
|
||||
ErrorDocument 404 /pdf/404.html
|
||||
```
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### WASM-404-Fehler
|
||||
|
||||
Stellen Sie sicher, dass der MIME-Typ konfiguriert ist:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Signieren oder Formularausfüllung zeigt einen leeren Viewer (`.mjs`-MIME-Fehler)
|
||||
|
||||
Wenn die Browserkonsole Folgendes anzeigt:
|
||||
|
||||
```
|
||||
Failed to load module script: The server responded with a non-JavaScript
|
||||
MIME type of "application/octet-stream". Strict MIME type checking is
|
||||
enforced for module scripts per HTML spec.
|
||||
```
|
||||
|
||||
...und die fehlgeschlagene Anfrage auf `.mjs` endet, fehlt in Ihrer Apache-Konfiguration die Zuordnung `.mjs → application/javascript`. Der mitgelieferte PDF-Viewer enthält ES-Moduldateien mit der Endung `.mjs`, die in der Standard-`mime.types` von Apache nicht enthalten sind.
|
||||
|
||||
Fügen Sie Folgendes zu Ihrem VirtualHost oder Ihrer `.htaccess` hinzu:
|
||||
|
||||
```apache
|
||||
AddType application/javascript .mjs
|
||||
```
|
||||
|
||||
Laden Sie Apache anschließend neu:
|
||||
|
||||
```bash
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
Zur Kontrolle: Suchen Sie in den Browser-DevTools im Tab Netzwerk die fehlgeschlagene `.mjs`-Anfrage und prüfen Sie den Antwort-Header `Content-Type`. Er sollte jetzt `application/javascript` lauten, nicht `application/octet-stream`.
|
||||
|
||||
### Rewrite funktioniert nicht
|
||||
|
||||
Prüfen Sie, ob mod_rewrite aktiviert ist:
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
### Word/ODT/Excel zu PDF funktioniert nicht
|
||||
|
||||
LibreOffice WASM benötigt `SharedArrayBuffer`, wofür diese Header erforderlich sind:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
Außerdem ist ein sicherer Kontext nötig. `http://localhost` funktioniert für lokale Tests, aber `http://192.168.x.x` oder andere LAN-IPs erfordern meist HTTPS. Sind die Header vorhanden und `window.crossOriginIsolated` ist trotzdem `false`, prüfen Sie, ob die Seite über einfaches HTTP von einem Nicht-Loopback-Origin aus geöffnet wird.
|
||||
|
||||
Die vorkomprimierten Dateien `.wasm.gz` und `.data.gz` brauchen außerdem das richtige `Content-Encoding`:
|
||||
|
||||
```apache
|
||||
<FilesMatch "soffice\.wasm\.gz$">
|
||||
ForceType application/wasm
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
<FilesMatch "soffice\.data\.gz$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
```
|
||||
|
||||
Stellen Sie sicher, dass `mod_headers` aktiviert ist: `sudo a2enmod headers`
|
||||
|
||||
### Zugriff verweigert
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
sudo chmod -R 755 /var/www/bentopdf
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
# Auf AWS S3 + CloudFront bereitstellen
|
||||
|
||||
Hosten Sie BentoPDF auf AWS für maximale Kontrolle und Skalierbarkeit.
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
User → CloudFront (CDN) → S3 (Static Files)
|
||||
```
|
||||
|
||||
## Schritt 1: S3-Bucket erstellen
|
||||
|
||||
```bash
|
||||
# Create bucket
|
||||
aws s3 mb s3://your-bentopdf-bucket --region us-east-1
|
||||
|
||||
# Enable static website hosting
|
||||
aws s3 website s3://your-bentopdf-bucket \
|
||||
--index-document index.html \
|
||||
--error-document index.html
|
||||
```
|
||||
|
||||
## Schritt 2: Bauen und hochladen
|
||||
|
||||
```bash
|
||||
# Build the project (optionally with custom branding)
|
||||
# VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
npm run build
|
||||
|
||||
# Sync to S3
|
||||
aws s3 sync dist/ s3://your-bentopdf-bucket \
|
||||
--delete \
|
||||
--cache-control "max-age=31536000"
|
||||
|
||||
# Set correct MIME types for WASM
|
||||
aws s3 cp s3://your-bentopdf-bucket/ s3://your-bentopdf-bucket/ \
|
||||
--recursive \
|
||||
--exclude "*" \
|
||||
--include "*.wasm" \
|
||||
--content-type "application/wasm" \
|
||||
--metadata-directive REPLACE
|
||||
|
||||
# Set correct MIME type for ES module files (.mjs)
|
||||
# S3 defaults .mjs to application/octet-stream, which browsers refuse to
|
||||
# execute as ES modules. The bundled PDF viewer (Sign PDF, Form Filler)
|
||||
# ships ES modules with .mjs extensions and will fail without this step.
|
||||
aws s3 cp s3://your-bentopdf-bucket/ s3://your-bentopdf-bucket/ \
|
||||
--recursive \
|
||||
--exclude "*" \
|
||||
--include "*.mjs" \
|
||||
--content-type "application/javascript" \
|
||||
--metadata-directive REPLACE
|
||||
```
|
||||
|
||||
## Schritt 3: CloudFront-Distribution erstellen
|
||||
|
||||
```bash
|
||||
aws cloudfront create-distribution \
|
||||
--origin-domain-name your-bentopdf-bucket.s3.amazonaws.com \
|
||||
--default-root-object index.html
|
||||
```
|
||||
|
||||
Oder nutzen Sie die AWS-Konsole:
|
||||
|
||||
1. Gehen Sie zu CloudFront → Create distribution
|
||||
2. Origin domain: Wählen Sie Ihren S3-Bucket aus
|
||||
3. Aktivieren Sie "Origin Access Control"
|
||||
4. Default root object: `index.html`
|
||||
5. Erstellen Sie die Distribution
|
||||
|
||||
## Schritt 3b: Response Headers Policy (erforderlich für LibreOffice WASM)
|
||||
|
||||
LibreOffice-basierte Konvertierungen (Word, Excel, PowerPoint zu PDF) benötigen `SharedArrayBuffer`, wofür bestimmte Response-Header nötig sind. Erstellen Sie eine CloudFront Response Headers Policy:
|
||||
|
||||
1. Gehen Sie zu CloudFront → Policies → Response headers
|
||||
2. Erstellen Sie eine eigene Policy mit diesen Headern:
|
||||
|
||||
| Header | Wert |
|
||||
| ------------------------------ | -------------- |
|
||||
| `Cross-Origin-Embedder-Policy` | `require-corp` |
|
||||
| `Cross-Origin-Opener-Policy` | `same-origin` |
|
||||
| `Cross-Origin-Resource-Policy` | `cross-origin` |
|
||||
|
||||
3. Weisen Sie die Policy dem Default Cache Behavior Ihrer Distribution zu
|
||||
|
||||
Oder per CLI:
|
||||
|
||||
```bash
|
||||
aws cloudfront create-response-headers-policy \
|
||||
--response-headers-policy-config '{
|
||||
"Name": "BentoPDF-COEP-COOP",
|
||||
"CustomHeadersConfig": {
|
||||
"Quantity": 3,
|
||||
"Items": [
|
||||
{"Header": "Cross-Origin-Embedder-Policy", "Value": "require-corp", "Override": true},
|
||||
{"Header": "Cross-Origin-Opener-Policy", "Value": "same-origin", "Override": true},
|
||||
{"Header": "Cross-Origin-Resource-Policy", "Value": "cross-origin", "Override": true}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Schritt 3c: S3-Metadaten für vorkomprimierte WASM-Dateien
|
||||
|
||||
Die LibreOffice-WASM-Dateien sind vorkomprimiert (`.wasm.gz`, `.data.gz`). Setzen Sie den richtigen Content-Type und das richtige Content-Encoding, damit Browser sie entpacken:
|
||||
|
||||
```bash
|
||||
# Set correct headers for soffice.wasm.gz
|
||||
aws s3 cp s3://your-bentopdf-bucket/libreoffice-wasm/soffice.wasm.gz \
|
||||
s3://your-bentopdf-bucket/libreoffice-wasm/soffice.wasm.gz \
|
||||
--content-type "application/wasm" \
|
||||
--content-encoding "gzip" \
|
||||
--metadata-directive REPLACE
|
||||
|
||||
# Set correct headers for soffice.data.gz
|
||||
aws s3 cp s3://your-bentopdf-bucket/libreoffice-wasm/soffice.data.gz \
|
||||
s3://your-bentopdf-bucket/libreoffice-wasm/soffice.data.gz \
|
||||
--content-type "application/octet-stream" \
|
||||
--content-encoding "gzip" \
|
||||
--metadata-directive REPLACE
|
||||
```
|
||||
|
||||
::: warning Wichtig
|
||||
Ohne die Response Headers Policy ist `SharedArrayBuffer` nicht verfügbar, und LibreOffice-WASM-Konvertierungen bleiben bei ca. 55% hängen. Ohne das richtige Content-Encoding für die `.gz`-Dateien erhält der Browser rohe gzip-Bytes, und die WASM-Kompilierung schlägt fehl.
|
||||
:::
|
||||
|
||||
## Schritt 4: S3-Bucket-Policy
|
||||
|
||||
Erlauben Sie CloudFront den Zugriff auf den Bucket:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowCloudFrontAccess",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "cloudfront.amazonaws.com"
|
||||
},
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::your-bentopdf-bucket/*",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Schritt 5: Eigene Fehlerseiten
|
||||
|
||||
Konfigurieren Sie 404 so, dass für das SPA-Routing `index.html` zurückgegeben wird:
|
||||
|
||||
1. CloudFront → Error pages
|
||||
2. Erstellen Sie eine eigene Fehlerantwort:
|
||||
- HTTP error code: 404
|
||||
- Response page path: `/index.html`
|
||||
- HTTP response code: 200
|
||||
|
||||
## Kostenschätzung
|
||||
|
||||
| Ressource | Geschätzte Kosten |
|
||||
| -------------------------- | ----------------- |
|
||||
| S3 Storage (~500MB) | ~$0.01/Monat |
|
||||
| CloudFront (1TB transfer) | ~$85/Monat |
|
||||
| CloudFront (10GB transfer) | ~$0.85/Monat |
|
||||
|
||||
::: tip
|
||||
Nutzen Sie S3 Intelligent Tiering zur Kostenoptimierung bei selten abgerufenen Dateien.
|
||||
:::
|
||||
|
||||
## Automatisierung mit Terraform
|
||||
|
||||
```hcl
|
||||
# main.tf
|
||||
resource "aws_s3_bucket" "bentopdf" {
|
||||
bucket = "your-bentopdf-bucket"
|
||||
}
|
||||
|
||||
resource "aws_cloudfront_distribution" "bentopdf" {
|
||||
origin {
|
||||
domain_name = aws_s3_bucket.bentopdf.bucket_regional_domain_name
|
||||
origin_id = "S3Origin"
|
||||
}
|
||||
|
||||
enabled = true
|
||||
default_root_object = "index.html"
|
||||
|
||||
default_cache_behavior {
|
||||
allowed_methods = ["GET", "HEAD"]
|
||||
cached_methods = ["GET", "HEAD"]
|
||||
target_origin_id = "S3Origin"
|
||||
|
||||
viewer_protocol_policy = "redirect-to-https"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,154 @@
|
||||
# Auf Cloudflare Pages bereitstellen
|
||||
|
||||
[Cloudflare Pages](https://pages.cloudflare.com) bietet schnelles, globales Hosting für statische Websites mit unbegrenzter Bandbreite.
|
||||
|
||||
## Schnelle Bereitstellung
|
||||
|
||||
1. Gehen Sie zu [Cloudflare Pages](https://dash.cloudflare.com/?to=/:account/pages)
|
||||
2. Klicken Sie auf "Create a project"
|
||||
3. Verbinden Sie Ihr GitHub-Repository
|
||||
|
||||
## Build-Konfiguration
|
||||
|
||||
| Einstellung | Wert |
|
||||
| ---------------------- | --------------- |
|
||||
| Framework preset | None |
|
||||
| Build command | `npm run build` |
|
||||
| Build output directory | `dist` |
|
||||
| Root directory | `/` |
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
Fügen Sie diese unter Settings → Environment variables hinzu:
|
||||
|
||||
| Variable | Wert |
|
||||
| ----------------------- | -------------------------------------------------- |
|
||||
| `NODE_VERSION` | `18` |
|
||||
| `SIMPLE_MODE` | `false` (optional) |
|
||||
| `VITE_BRAND_NAME` | Eigener Markenname (optional) |
|
||||
| `VITE_BRAND_LOGO` | Logo-Pfad relativ zu `public/` (optional) |
|
||||
| `VITE_FOOTER_TEXT` | Eigener Footer-/Copyright-Text (optional) |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standard-Oberflächensprache, z. B. `fr` (optional) |
|
||||
|
||||
## Konfigurationsdatei
|
||||
|
||||
Erstellen Sie `_headers` in Ihrem Ordner `public`:
|
||||
|
||||
```
|
||||
# Required security headers for SharedArrayBuffer (used by LibreOffice WASM)
|
||||
/*
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
||||
|
||||
# Pre-compressed LibreOffice WASM binary
|
||||
/libreoffice-wasm/soffice.wasm.gz
|
||||
Content-Type: application/wasm
|
||||
Content-Encoding: gzip
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
# Pre-compressed LibreOffice WASM data
|
||||
/libreoffice-wasm/soffice.data.gz
|
||||
Content-Type: application/octet-stream
|
||||
Content-Encoding: gzip
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
# Cache WASM files aggressively
|
||||
/*.wasm
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
Content-Type: application/wasm
|
||||
|
||||
# Service worker
|
||||
/sw.js
|
||||
Cache-Control: no-cache
|
||||
```
|
||||
|
||||
::: warning Wichtig
|
||||
Die Header `Cross-Origin-Embedder-Policy` und `Cross-Origin-Opener-Policy` sind für Konvertierungen von Word/ODT/Excel/PowerPoint zu PDF erforderlich. Ohne sie ist `SharedArrayBuffer` nicht verfügbar, und die LibreOffice-WASM-Engine kann nicht initialisiert werden.
|
||||
:::
|
||||
|
||||
Erstellen Sie `_redirects` für das SPA-Routing:
|
||||
|
||||
```
|
||||
/* /index.html 200
|
||||
```
|
||||
|
||||
## Eigene Domain
|
||||
|
||||
1. Öffnen Sie Ihr Pages-Projekt
|
||||
2. Klicken Sie auf "Custom domains"
|
||||
3. Fügen Sie Ihre Domain hinzu
|
||||
4. Cloudflare konfiguriert das DNS automatisch, wenn die Domain bei Cloudflare liegt
|
||||
|
||||
## Vorteile
|
||||
|
||||
- **Kostenlose, unbegrenzte Bandbreite**
|
||||
- **Globales CDN** mit über 300 Edge-Standorten
|
||||
- **Automatisches HTTPS**
|
||||
- **Vorschau-Bereitstellungen** für Pull Requests
|
||||
- **Schnelle Builds**
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Upload großer Dateien
|
||||
|
||||
Cloudflare Pages unterstützt Dateien bis 25 MB. WASM-Module sollten kein Problem sein, aber wenn Sie an Grenzen stoßen, sollten Sie Folgendes überlegen:
|
||||
|
||||
```bash
|
||||
# Split large files during build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Größenlimits für Worker
|
||||
|
||||
Wenn Sie Cloudflare Workers für erweitertes Routing nutzen, beachten Sie das Limit von 1 MB in kostenlosen Tarifen.
|
||||
|
||||
## CORS-Proxy-Worker (für digitale Signaturen)
|
||||
|
||||
Das Werkzeug für digitale Signaturen benötigt einen CORS-Proxy, um Zertifikatsketten abzurufen. Stellen Sie den mitgelieferten Worker bereit:
|
||||
|
||||
```bash
|
||||
cd cloudflare
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Sicherheitsfunktionen
|
||||
|
||||
| Funktion | Beschreibung |
|
||||
| ---------------------------- | ----------------------------------- |
|
||||
| **URL-Beschränkungen** | Nur Zertifikat-URLs erlaubt |
|
||||
| **Dateigrößenlimit** | Max. 10MB pro Anfrage |
|
||||
| **Ratenbegrenzung** | 60 Anfr./IP/Min. (erfordert KV) |
|
||||
| **Blockierung privater IPs** | Blockiert localhost und interne IPs |
|
||||
|
||||
### Ratenbegrenzung aktivieren
|
||||
|
||||
```bash
|
||||
# Create KV namespace
|
||||
npx wrangler kv namespace create "RATE_LIMIT_KV"
|
||||
|
||||
# Add to wrangler.toml with returned ID:
|
||||
# [[kv_namespaces]]
|
||||
# binding = "RATE_LIMIT_KV"
|
||||
# id = "YOUR_ID"
|
||||
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Mit Proxy-URL bauen
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
Oder mit Docker:
|
||||
|
||||
```bash
|
||||
export VITE_CORS_PROXY_URL="https://your-worker.workers.dev"
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
--secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \
|
||||
-t your-bentopdf .
|
||||
```
|
||||
|
||||
> **Hinweis:** Die Einrichtung der HMAC-Signatur finden Sie in der [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required).
|
||||
@@ -0,0 +1,180 @@
|
||||
# CORS-Proxy zum Abrufen von Zertifikaten
|
||||
|
||||
Das Werkzeug für digitale Signaturen nutzt einen CORS-Proxy, um Aussteller-Zertifikate von externen Zertifizierungsstellen (CAs) abzurufen. Das ist nötig, weil viele CA-Server keine CORS-Header in ihren Antworten mitsenden, wodurch ein direkter Abruf aus dem Browser verhindert wird.
|
||||
|
||||
Außerdem liefern viele CA-Server Zertifikate über einfaches HTTP aus. Wird Ihre BentoPDF-Instanz über HTTPS gehostet, blockieren Browser diese HTTP-Anfragen (Mixed-Content-Richtlinie). Der CORS-Proxy löst beide Probleme, indem er Anfragen über einen HTTPS-Endpunkt mit den passenden Headern leitet.
|
||||
|
||||
## So funktioniert es
|
||||
|
||||
Beim Signieren einer PDF mit einem Zertifikat:
|
||||
|
||||
1. Die Bibliothek `zgapdfsigner` versucht, eine vollständige Zertifikatskette aufzubauen
|
||||
2. Sie ruft Aussteller-Zertifikate von URLs ab, die in der AIA-Erweiterung (Authority Information Access) Ihres Zertifikats eingebettet sind
|
||||
3. Diese Anfragen laufen über einen CORS-Proxy, der die nötigen `Access-Control-Allow-Origin`-Header ergänzt
|
||||
4. Der Proxy liefert die Zertifikatsdaten an den Browser zurück
|
||||
|
||||
## Den CORS-Proxy selbst hosten
|
||||
|
||||
Wenn Sie BentoPDF selbst hosten, müssen Sie einen eigenen CORS-Proxy bereitstellen, damit digitale Signaturen mit Zertifikaten funktionieren, die einen Abruf der Kette erfordern.
|
||||
|
||||
### Option 1: Cloudflare Workers (empfohlen)
|
||||
|
||||
1. **Wrangler CLI installieren**:
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
```
|
||||
|
||||
2. **Bei Cloudflare anmelden**:
|
||||
|
||||
```bash
|
||||
wrangler login
|
||||
```
|
||||
|
||||
3. **BentoPDF klonen und erlaubte Origins anpassen**:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf/cloudflare
|
||||
```
|
||||
|
||||
Öffnen Sie `cors-proxy-worker.js` und änderen Sie das Array `ALLOWED_ORIGINS` auf Ihre Domain:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = [
|
||||
'https://your-domain.com',
|
||||
'https://www.your-domain.com',
|
||||
];
|
||||
```
|
||||
|
||||
::: warning Wichtig
|
||||
Ohne diese Änderung lehnt der Proxy alle Anfragen von Ihrer Website mit dem Fehler **403 Forbidden** ab. Standardmäßig sind nur Anfragen von `bentopdf.com` erlaubt.
|
||||
:::
|
||||
|
||||
4. **Den Proxy bereitstellen**:
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Notieren Sie sich die URL Ihres Workers (z. B. `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
|
||||
|
||||
5. **BentoPDF mit der Proxy-URL neu bauen**:
|
||||
|
||||
Bei Verwendung von Docker:
|
||||
|
||||
```bash
|
||||
export VITE_CORS_PROXY_URL="https://your-worker.workers.dev"
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
--secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \
|
||||
-t your-bentopdf .
|
||||
```
|
||||
|
||||
Beim Bauen aus dem Quellcode:
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
### Option 2: Eigener Backend-Proxy
|
||||
|
||||
Sie können auch einen eigenen Proxy-Endpunkt erstellen. Die Anforderungen sind:
|
||||
|
||||
1. GET-Anfragen mit einem Query-Parameter `url` annehmen
|
||||
2. Die URL von Ihrem Server aus abrufen (serverseitig gibt es keine CORS-Beschränkungen)
|
||||
3. Die Antwort mit diesen Headern zurückgeben:
|
||||
- `Access-Control-Allow-Origin: https://your-domain.com`
|
||||
- `Access-Control-Allow-Methods: GET, OPTIONS`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
|
||||
Beispielimplementierung mit Express.js:
|
||||
|
||||
```javascript
|
||||
app.get('/api/cert-proxy', async (req, res) => {
|
||||
const targetUrl = req.query.url;
|
||||
|
||||
// Validate it's a certificate URL
|
||||
if (!isValidCertUrl(targetUrl)) {
|
||||
return res.status(400).json({ error: 'Invalid URL' });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(targetUrl);
|
||||
const data = await response.arrayBuffer();
|
||||
|
||||
res.set('Access-Control-Allow-Origin', 'https://your-domain.com');
|
||||
res.set('Content-Type', 'application/octet-stream');
|
||||
res.set('X-Content-Type-Options', 'nosniff');
|
||||
res.send(Buffer.from(data));
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Proxy error' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Sicherheitshinweise
|
||||
|
||||
Der mitgelieferte Cloudflare Worker hat mehrere Sicherheitsmaßnahmen:
|
||||
|
||||
| Funktion | Beschreibung |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| **Origin-Prüfung** | Erlaubt nur Anfragen von Domains aus `ALLOWED_ORIGINS` |
|
||||
| **URL-Beschränkungen** | Erlaubt nur Zertifikat-URLs (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`) |
|
||||
| **Blockierung privater IPs** | Blockiert private IPv4-/IPv6-Bereiche, Link-Local, Loopback, dezimale IPs und Cloud-Metadaten |
|
||||
| **Content-Type-Sicherheit** | Liefert nur sicheren Sie Zertifikat-MIME-Typen, blockiert Content-Type-Injection vom Upstream |
|
||||
| **Dateigrößenlimit** | Streamt die Antwort mit 10MB-Limit und bricht den Download bei Überschreitung ab |
|
||||
| **Ratenbegrenzung** | 60 Anfragen pro Minute pro IP oder IPv6 /64 (erfordert KV, bei KV-Fehlern wird nicht blockiert) |
|
||||
| **HMAC-Signaturen** | Optionale clientseitige Signierung (schreckt gelegentlichen Missbrauch ab) |
|
||||
|
||||
### DNS-Rebinding (Selbst-Hosting außerhalb von Cloudflare)
|
||||
|
||||
Der Worker lehnt jede URL ab, die auf eine private oder interne IP aufgelöst wird. Eine feste Allowlist von Hosts ist nicht möglich, da Zertifikatsketten-URLs auf die jeweilige CA verweisen, die das Zertifikat ausgestellt hat (FNMT, Unternehmens-CAs usw.), die Ziele also nicht im Voraus bekannt sind. Dadurch bleibt ein kleines Rebinding-Fenster: Wer das DNS für seine eigene Domain betreibt, könnte der Sicherheitsprüfung eine öffentliche IP liefern und der eigentlichen Anfrage einen Moment später eine private.
|
||||
|
||||
Auf Cloudflare ist das nicht ausnutzbar: Workers können weder private IPs noch den Metadaten-Endpunkt `169.254.169.254` erreichen, ein Rebind läuft also ins Leere. Geben Sie dem Worker nur keinen Zugang: Hängen Sie keinen Tunnel, Service oder kein Private-Network-Binding an (er braucht nur `RATE_LIMIT_KV`).
|
||||
|
||||
Betreiben Sie ihn außerhalb von Cloudflare (workerd, Node und Ähnliches) in einer VPC, _kann_ die Runtime interne IPs erreichen. Setzen Sie eine Egress-Firewall davor und blockieren Sie die privaten Bereiche: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `127.0.0.0/8`, `169.254.0.0/16` sowie die IPv6-Entsprechungen. Anwendungscode kann das außerhalb von Cloudflare nicht durchsetzen.
|
||||
|
||||
## Den Proxy deaktivieren
|
||||
|
||||
Wenn Sie keinen CORS-Proxy nutzen möchten, setzen Sie die Umgebungsvariable auf einen leeren String:
|
||||
|
||||
```
|
||||
VITE_CORS_PROXY_URL=
|
||||
```
|
||||
|
||||
**Hinweis**: Ohne den Proxy schlägt das Signieren mit Zertifikaten, die einen externen Abruf der Kette erfordern (wie FNMT oder einige Unternehmens-CAs), mit dem Fehler "Failed to fetch" fehl.
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### "Signing error: TypeError: Failed to fetch"
|
||||
|
||||
Das bedeutet meist eines von beidem:
|
||||
|
||||
1. **Kein CORS-Proxy konfiguriert**: Setzen Sie `VITE_CORS_PROXY_URL` und bauen Sie neu
|
||||
2. **Mixed Content blockiert**: Ihre Website nutzt HTTPS, aber die Aussteller-URL des Zertifikats ist HTTP. Der CORS-Proxy löst das.
|
||||
3. **CORS-Proxy lehnt Ihren Origin ab**: Prüfen Sie, ob Ihre Domain im Array `ALLOWED_ORIGINS` in `cors-proxy-worker.js` steht
|
||||
|
||||
### "403 Forbidden" vom Proxy
|
||||
|
||||
Ihre Domain steht nicht in der Liste `ALLOWED_ORIGINS`. Bearbeiten Sie `cors-proxy-worker.js`:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = ['https://your-domain.com'];
|
||||
```
|
||||
|
||||
Stellen Sie danach erneut bereit: `npx wrangler deploy`
|
||||
|
||||
### Den Proxy testen
|
||||
|
||||
```bash
|
||||
curl -H "Origin: https://your-domain.com" \
|
||||
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"
|
||||
```
|
||||
|
||||
### Zertifikate, die ohne Proxy funktionieren
|
||||
|
||||
Manche Zertifikate enthalten die vollständige Kette in der P12/PFX-Datei und benötigen keinen externen Abruf:
|
||||
|
||||
- Selbstsignierte Zertifikate
|
||||
- Einige kommerzielle CAs, die Zwischenzertifikate mitliefern
|
||||
- Zertifikate, die Sie manuell mit der vollständigen Kette zusammengestellt haben
|
||||
@@ -0,0 +1,622 @@
|
||||
# Mit Docker / Podman bereitstellen
|
||||
|
||||
Der einfachste Weg, BentoPDF in einer Produktionsumgebung selbst zu hosten.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Erforderliche Header für die Konvertierung von Office-Dateien**
|
||||
>
|
||||
> LibreOffice-basierte Werkzeuge (Word-, Excel-, PowerPoint-Konvertierung) benötigen diese HTTP-Header für die `SharedArrayBuffer`-Unterstützung:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Die Seite muss außerdem aus einem sicheren Kontext ausgeliefert werden. `http://localhost` funktioniert für lokale Tests, aber `http://192.168.x.x` oder andere LAN-IPs gelten meist nicht als sicher, daher schlägt die Office-Konvertierung über einfaches HTTP fehl, auch wenn die Header vorhanden sind.
|
||||
>
|
||||
> Die offiziellen Container-Images enthalten diese Header. Wenn Sie einen Reverse-Proxy (Traefik, Caddy usw.) verwenden, stellen Sie sicher, dass diese Header erhalten bleiben oder hinzugefügt werden, und nutzen Sie HTTPS für Zugriffe außerhalb von Loopback.
|
||||
|
||||
> [!TIP]
|
||||
> **Podman-Nutzer:** Alle `docker`-Befehle funktionieren mit Podman, wenn Sie `docker` durch `podman` und `docker-compose` durch `podman-compose` ersetzen.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF gibt es in zwei Builds:
|
||||
>
|
||||
> - **Self-Hosted-Build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Jedes PDF-Werkzeug, **ohne** Marketing-Elemente (kein Hero, keine FAQ, keine Testimonials, kein Footer). Nutzen Sie ihn für interne Bereitstellungen in Teams und Organisationen. Er ist **keine** funktionsreduzierte "Lite"-Version.
|
||||
> - **Commercial-Build**: `ghcr.io/alam00000/bentopdf:latest`. Die vollständige Marketing-Website, die bentopdf.com selbst und Inhaber kommerzieller Lizenzen für öffentlich zugängliche Bereitstellungen nutzen.
|
||||
>
|
||||
> Im Zweifel: Nehmen Sie den Self-Hosted-Build.
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker run -d \
|
||||
--name bentopdf \
|
||||
-p 3000:8080 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
|
||||
# Podman
|
||||
podman run -d \
|
||||
--name bentopdf \
|
||||
-p 3000:8080 \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
## Docker Compose / Podman Compose
|
||||
|
||||
Erstellen Sie `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
container_name: bentopdf
|
||||
ports:
|
||||
- '3000:8080'
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:8080']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
Ausführen:
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Eigenes Image bauen
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 8080
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
Bauen und ausführen:
|
||||
|
||||
```bash
|
||||
docker build -t bentopdf:custom .
|
||||
docker run -d -p 3000:8080 bentopdf:custom
|
||||
```
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
| Variable | Beschreibung | Standard |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Baut die Self-Hosted-Variante (blendet die Marketing-Elemente von bentopdf.com aus: Hero, FAQ, Testimonials, Footer). Alle PDF-Werkzeuge bleiben erhalten. | `false` |
|
||||
| `BASE_URL` | In Unterverzeichnis bereitstellen | `/` |
|
||||
| `VITE_WASM_PYMUPDF_URL` | URL des PyMuPDF-WASM-Moduls | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/` |
|
||||
| `VITE_WASM_GS_URL` | URL des Ghostscript-WASM-Moduls | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/` |
|
||||
| `VITE_WASM_CPDF_URL` | URL des CoherentPDF-WASM-Moduls | `https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/` |
|
||||
| `VITE_TESSERACT_WORKER_URL` | URL des OCR-Worker-Skripts | _(leer; Standard-CDN von Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_CORE_URL` | Verzeichnis der OCR-Core-Runtime | _(leer; Standard-CDN von Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_LANG_URL` | Verzeichnis der OCR-traineddata | _(leer; Standard-CDN von Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_AVAILABLE_LANGUAGES` | Kommagetrennte OCR-Sprachen, die in der Oberfläche angeboten werden | _(leer; vollständigen Katalog anzeigen)_ |
|
||||
| `VITE_OCR_FONT_BASE_URL` | Verzeichnis der OCR-Textebenen-Schriften | _(leer; externe Noto-Schrift-URLs)_ |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standard UI language | `en` |
|
||||
| `VITE_BRAND_NAME` | Eigener Markenname | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logo-Pfad relativ zu `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Eigener Footer-/Copyright-Text | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | Kommagetrennte Werkzeug-IDs zum Ausblenden | _(leer; alle Werkzeuge aktiviert)_ |
|
||||
| `DISABLE_GITHUB_STARS` | Überspringt den Abruf der GitHub-Sterne beim Laden der Seite und entfernt `api.github.com` aus der CSP. Simple-Mode-Builds überspringen ihn immer. | `false` |
|
||||
| `SITE_URL` | Öffentliche URL, die in Canonical-, hreflang- und Sitemap-Links geschrieben wird. Lassen Sie den Standard, sofern Suchmaschinen Ihre Bereitstellung nicht als eigene Website behandeln sollen. | `https://www.bentopdf.com` |
|
||||
| `ALLOW_INDEXING` | Bei einer eigenen `SITE_URL` wird jede Seite mit `noindex` markiert, damit Ihre Kopie in den Suchergebnissen nicht mit bentopdf.com konkurriert. Mit `true` dürfen Suchmaschinen sie indexieren. | `false` |
|
||||
|
||||
Die WASM-Modul-URLs sind mit CDN-Standardwerten vorkonfiguriert, sodass alle erweiterten Funktionen sofort funktionieren. Überschreiben Sie sie für abgeschottete oder selbst gehostete Bereitstellungen.
|
||||
|
||||
### Suchmaschinen
|
||||
|
||||
Standardmäßig verweist jede Seite Suchmaschinen auf bentopdf.com als Original, sodass Ihre Bereitstellung nicht als Duplikat erscheint. Für eine private oder interne Instanz müssen Sie nichts ändern.
|
||||
|
||||
- **Ihre Instanz komplett aus den Suchergebnissen heraushalten:** Starten Sie den Container mit `-e ROBOTS_NOINDEX=true`. Das erfordert nur einen Neustart, keinen Rebuild.
|
||||
- **Ihre Instanz als eigene Website in den Suchergebnissen erscheinen lassen:** Bauen Sie mit `--build-arg SITE_URL=https://your-domain.example --build-arg ALLOW_INDEXING=true`. Ohne `ALLOW_INDEXING=true` markiert eine eigene `SITE_URL` jede Seite mit `noindex`.
|
||||
|
||||
### Content-Security-Policy
|
||||
|
||||
Das nginx-Image wird mit einem erzwingenden `Content-Security-Policy`-Header ausgeliefert. Die Direktiven `script-src`, `connect-src` und `font-src` der CSP werden **beim Build** aus den oben genannten `VITE_*`-URL-Variablen **generiert**: Alle Hosts, die Sie per `--build-arg` übergeben, werden automatisch in die Richtlinie aufgenommen.
|
||||
|
||||
Daraus folgt:
|
||||
|
||||
- Wenn Sie `VITE_CORS_PROXY_URL` oder `VITE_WASM_*_URL` beim Build überschreiben, erlaubt die CSP diese Origins automatisch, ohne zusätzliche Konfiguration.
|
||||
- Für URLs auf `cdn.jsdelivr.net` erlaubt `script-src` nur den exakten versionierten Paketordner, zum Beispiel `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/`, nicht das gesamte CDN. jsDelivr liefert jedes öffentliche npm-Paket und GitHub-Repository aus, sodass die Freigabe des gesamten Hosts es jedem davon erlauben würde, auf Ihrer Instanz zu laufen. URLs auf allen anderen Hosts werden per Origin erlaubt.
|
||||
- Wenn Sie eigene WASM-URLs zur _Laufzeit_ über die Seite Erweiterte Einstellungen in der App konfigurieren, stehen diese Origins **nicht** in der CSP, und der Browser blockiert Abrufe dorthin. Das gilt auch für eine andere Version desselben Pakets auf `cdn.jsdelivr.net`, da nur die beim Build festgelegte Version erlaubt ist. Die Konfiguration zur Laufzeit ist für Experimente gedacht; für dauerhafte eigene URLs setzen Sie das passende `VITE_*`-Build-Argument.
|
||||
- Bei abgeschotteten Bereitstellungen, die alle drei `VITE_WASM_*_URL`-Werte überschreiben, werden auch diese `cdn.jsdelivr.net`-Paketordner aus der CSP entfernt (jeder Standardwert wird ersetzt, nicht ergänzt). Die Tesseract-OCR-Standardwerte auf `cdn.jsdelivr.net` bleiben in der Richtlinie, bis Sie auch die drei `VITE_TESSERACT_*`-Werte setzen. Ebenso ersetzt das Setzen von `VITE_CORS_PROXY_URL` den öffentlichen Standard `bentopdf-cors-proxy.bentopdf.workers.dev`.
|
||||
|
||||
Die CSP enthält `'unsafe-eval'` in `script-src`, weil die LibreOffice-WASM-Runtime (für die Word-/Excel-/PowerPoint-Konvertierung) und die Bildbibliothek wasm-vips (für PDF zu TIFF) internen Dispatch-Code über `new Function()` kompilieren. Ohne es würden diese Werkzeuge nicht mehr funktionieren. Wenn Sie im `SIMPLE_MODE` (ohne LibreOffice) bauen und PDF zu TIFF nicht brauchen, können Sie die generierte `security-headers.conf` manuell bearbeiten und `'unsafe-eval'` für eine strengere Richtlinie entfernen.
|
||||
|
||||
Lassen Sie für OCR die `VITE_TESSERACT_*`-Variablen leer, um die Standard-Online-Ressourcen zu nutzen, oder setzen Sie alle drei zusammen für selbst gehostetes/Offline-OCR. Teilweise Überschreibungen werden abgelehnt, weil Worker, Core-Runtime und traineddata-Verzeichnis zusammenpassen müssen. Für eine vollständig offline erzeugte durchsuchbare PDF setzen Sie zusätzlich `VITE_OCR_FONT_BASE_URL`, damit die OCR-Textebenen-Schriften von Ihrem internen Server statt von den öffentlichen Noto-Schrift-URLs geladen werden.
|
||||
|
||||
`VITE_DEFAULT_LANGUAGE` legt die Oberflächensprache für Erstbesucher fest. Unterstützte Werte: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Nutzer können die Sprache weiterhin wechseln, das ändert nur den Standard.
|
||||
|
||||
Beispiel:
|
||||
|
||||
```bash
|
||||
# Build with French as the default language
|
||||
docker build --build-arg VITE_DEFAULT_LANGUAGE=fr -t bentopdf .
|
||||
docker run -d -p 3000:8080 bentopdf
|
||||
```
|
||||
|
||||
### Eigenes Branding
|
||||
|
||||
Ersetzen Sie das Standard-Logo, den Namen und den Footer-Text von BentoPDF durch Ihre eigenen. Legen Sie Ihre Logodatei im Ordner `public/` ab (oder nutzen Sie ein vorhandenes Bild) und übergib die Branding-Variablen beim Build:
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg VITE_BRAND_NAME="AcmePDF" \
|
||||
--build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
--build-arg VITE_FOOTER_TEXT="© 2026 Acme Corp. Internal use only." \
|
||||
-t acmepdf .
|
||||
```
|
||||
|
||||
Das Branding funktioniert im Vollmodus und im Simple Mode und lässt sich mit allen anderen Build-Optionen kombinieren.
|
||||
|
||||
### Bestimmte Werkzeuge deaktivieren
|
||||
|
||||
Blenden Sie Werkzeuge aus der Oberfläche aus, etwa aus Compliance- oder Sicherheitsgründen. Deaktivierte Werkzeuge werden von der Startseite, aus den Suchergebnissen, den Tastenkürzeln und dem Workflow-Builder entfernt. Beim direkten Aufruf der URL erscheint eine Seite "Werkzeug nicht verfügbar".
|
||||
|
||||
Werkzeug-IDs entsprechen der Seiten-URL ohne `.html`. Liegt das Werkzeug zum Beispiel unter `bentopdf.com/edit-pdf.html`, lautet die ID `edit-pdf`.
|
||||
|
||||
#### Werkzeug-IDs finden
|
||||
|
||||
Am einfachsten: Öffnen Sie ein beliebiges Werkzeug in BentoPDF und sehen Sie sich die URL an. Der letzte Teil des Pfads (ohne `.html`) ist die Werkzeug-ID.
|
||||
|
||||
<details>
|
||||
<summary>Vollständige Liste der Werkzeug-IDs</summary>
|
||||
|
||||
**Bearbeiten & Annotieren:** `edit-pdf`, `bookmark`, `table-of-contents`, `page-numbers`, `add-page-labels`, `bates-numbering`, `add-watermark`, `header-footer`, `invert-colors`, `scanner-effect`, `adjust-colors`, `background-color`, `text-color`, `sign-pdf`, `add-stamps`, `remove-annotations`, `crop-pdf`, `form-filler`, `form-creator`, `remove-blank-pages`
|
||||
|
||||
**In PDF konvertieren:** `image-to-pdf`, `jpg-to-pdf`, `png-to-pdf`, `webp-to-pdf`, `svg-to-pdf`, `bmp-to-pdf`, `heic-to-pdf`, `tiff-to-pdf`, `txt-to-pdf`, `markdown-to-pdf`, `json-to-pdf`, `csv-to-pdf`, `rtf-to-pdf`, `odt-to-pdf`, `word-to-pdf`, `excel-to-pdf`, `powerpoint-to-pdf`, `xps-to-pdf`, `mobi-to-pdf`, `epub-to-pdf`, `fb2-to-pdf`, `cbz-to-pdf`, `wpd-to-pdf`, `wps-to-pdf`, `xml-to-pdf`, `pages-to-pdf`, `odg-to-pdf`, `ods-to-pdf`, `odp-to-pdf`, `pub-to-pdf`, `vsd-to-pdf`, `psd-to-pdf`, `email-to-pdf`
|
||||
|
||||
**Aus PDF konvertieren:** `pdf-to-jpg`, `pdf-to-png`, `pdf-to-webp`, `pdf-to-bmp`, `pdf-to-tiff`, `pdf-to-cbz`, `pdf-to-svg`, `pdf-to-csv`, `pdf-to-excel`, `pdf-to-greyscale`, `pdf-to-json`, `pdf-to-docx`, `extract-images`, `pdf-to-markdown`, `prepare-pdf-for-ai`, `pdf-to-text`
|
||||
|
||||
**Organisieren & Verwalten:** `ocr-pdf`, `merge-pdf`, `alternate-merge`, `organize-pdf`, `add-attachments`, `extract-attachments`, `edit-attachments`, `pdf-multi-tool`, `pdf-layers`, `extract-tables`, `split-pdf`, `divide-pages`, `extract-pages`, `delete-pages`, `add-blank-page`, `reverse-pages`, `rotate-pdf`, `rotate-custom`, `n-up-pdf`, `pdf-booklet`, `combine-single-page`, `view-metadata`, `edit-metadata`, `pdf-to-zip`, `compare-pdfs`, `posterize-pdf`, `page-dimensions`
|
||||
|
||||
**Optimieren & Reparieren:** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Sicherheit:** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Option 1: Zur Build-Zeit (Docker-Build-Argument)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
Dadurch wird die Liste der deaktivierten Werkzeuge fest in das JavaScript-Bundle geschrieben. Zum Ändern ist ein Rebuild nötig.
|
||||
|
||||
#### Option 2: Zur Laufzeit (config.json)
|
||||
|
||||
Binden Sie eine `config.json`-Datei in das ausgelieferte Verzeichnis ein, ganz ohne Rebuild:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 3000:8080 \
|
||||
-v ./config.json:/usr/share/nginx/html/config.json:ro \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
Oder mit Docker Compose:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ports:
|
||||
- '3000:8080'
|
||||
volumes:
|
||||
- ./config.json:/usr/share/nginx/html/config.json:ro
|
||||
```
|
||||
|
||||
Beide Methoden lassen sich kombinieren, die Listen werden zusammengeführt. Taucht ein Werkzeug in einer von beiden auf, ist es deaktiviert.
|
||||
|
||||
#### Editor-Funktionen deaktivieren
|
||||
|
||||
Sie können auch einzelne Funktionen im PDF-Editor (z. B. Schwärzen, Annotationen, Formulare) deaktivieren, ohne das gesamte Editor-Werkzeug abzuschalten. Fügen Sie `editorDisabledCategories` zu Ihrer `config.json` hinzu:
|
||||
|
||||
```json
|
||||
{
|
||||
"editorDisabledCategories": ["redaction", "annotation-stamp"]
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Vollständige Liste der Editor-Kategorien</summary>
|
||||
|
||||
**Zoom:** `zoom`, `zoom-in`, `zoom-out`, `zoom-fit-page`, `zoom-fit-width`, `zoom-marquee`, `zoom-level`
|
||||
|
||||
**Annotation:** `annotation`, `annotation-markup`, `annotation-highlight`, `annotation-underline`, `annotation-strikeout`, `annotation-squiggly`, `annotation-ink`, `annotation-text`, `annotation-stamp`
|
||||
|
||||
**Formen:** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
|
||||
|
||||
**Formular:** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
|
||||
|
||||
**Schwärzung:** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
|
||||
|
||||
**Dokument:** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
|
||||
|
||||
**Seite:** `page`, `spread`, `rotate`, `scroll`, `navigation`
|
||||
|
||||
**Panel:** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
|
||||
|
||||
**Werkzeuge:** `tools`, `pan`, `pointer`, `capture`
|
||||
|
||||
**Auswahl:** `selection`, `selection-copy`
|
||||
|
||||
**Verlauf:** `history`, `history-undo`, `history-redo`
|
||||
|
||||
</details>
|
||||
|
||||
Kategorien sind hierarchisch: Das Deaktivieren einer übergeordneten Kategorie (z. B. `annotation`) deaktiviert alle ihre untergeordneten.
|
||||
|
||||
### Den GitHub-Sternezähler deaktivieren
|
||||
|
||||
Die Navigationsleiste zeigt einen Live-Zähler der GitHub-Sterne, der beim Laden der Seite von `api.github.com` abgerufen wird. Es ist die einzige externe Anfrage, die keiner PDF-Funktion dient. Deaktivieren Sie ihn beim Build, um den Abrufcode aus dem Bundle zu entfernen und `api.github.com` aus der Content-Security-Policy zu streichen:
|
||||
|
||||
```bash
|
||||
docker build --build-arg DISABLE_GITHUB_STARS=true -t bentopdf .
|
||||
```
|
||||
|
||||
Simple-Mode-Builds (`bentopdf-simple`) überspringen den Sternezähler immer. Dieses Flag wird nur für den Commercial-Build oder eigene Voll-Builds benötigt.
|
||||
|
||||
### Eigene WASM-URLs (abgeschottet / selbst gehostet)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> WASM-URLs werden **beim Build** fest in das JavaScript geschrieben. Die WASM-Dateien lädt der **Browser des Nutzers** zur Laufzeit herunter, Docker lädt sie beim Build nicht herunter. In abgeschotteten Netzwerken müssen Sie die WASM-Dateien auf einem internen Server hosten, den Browser erreichen können.
|
||||
|
||||
**Vollständiger Workflow für abgeschottete Umgebungen:**
|
||||
|
||||
```bash
|
||||
# 1. On a machine WITH internet — download WASM packages
|
||||
bash scripts/prepare-airgap.sh --list-ocr-languages
|
||||
bash scripts/prepare-airgap.sh --search-ocr-language german
|
||||
|
||||
# 2. Download WASM/OCR packages
|
||||
npm pack @bentopdf/pymupdf-wasm@0.11.14
|
||||
npm pack @bentopdf/gs-wasm
|
||||
npm pack coherentpdf
|
||||
npm pack tesseract.js@7.0.0
|
||||
npm pack tesseract.js-core@7.0.0
|
||||
mkdir -p tesseract-langdata
|
||||
curl -fsSL https://cdn.jsdelivr.net/npm/@tesseract.js-data/eng/4.0.0_best_int/eng.traineddata.gz -o tesseract-langdata/eng.traineddata.gz
|
||||
mkdir -p ocr-fonts
|
||||
curl -fsSL https://raw.githack.com/googlefonts/noto-fonts/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf -o ocr-fonts/NotoSans-Regular.ttf
|
||||
|
||||
# 3. Build the image with your internal server URLs
|
||||
docker build \
|
||||
--build-arg VITE_WASM_PYMUPDF_URL=https://internal-server.example.com/wasm/pymupdf/ \
|
||||
--build-arg VITE_WASM_GS_URL=https://internal-server.example.com/wasm/gs/ \
|
||||
--build-arg VITE_WASM_CPDF_URL=https://internal-server.example.com/wasm/cpdf/ \
|
||||
--build-arg VITE_TESSERACT_WORKER_URL=https://internal-server.example.com/wasm/ocr/worker.min.js \
|
||||
--build-arg VITE_TESSERACT_CORE_URL=https://internal-server.example.com/wasm/ocr/core \
|
||||
--build-arg VITE_TESSERACT_LANG_URL=https://internal-server.example.com/wasm/ocr/lang-data \
|
||||
--build-arg VITE_TESSERACT_AVAILABLE_LANGUAGES=eng,deu \
|
||||
--build-arg VITE_OCR_FONT_BASE_URL=https://internal-server.example.com/wasm/ocr/fonts \
|
||||
-t bentopdf .
|
||||
|
||||
# 4. Export the image
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
|
||||
# 5. Transfer bentopdf.tar + the .tgz packages + tesseract-langdata/ + ocr-fonts/ into the air-gapped network
|
||||
|
||||
# 6. Inside the air-gapped network — load and run
|
||||
docker load -i bentopdf.tar
|
||||
|
||||
# Extract WASM packages to your internal web server
|
||||
mkdir -p /var/www/wasm/pymupdf /var/www/wasm/gs /var/www/wasm/cpdf /var/www/wasm/ocr/core /var/www/wasm/ocr/lang-data /var/www/wasm/ocr/fonts
|
||||
tar xzf bentopdf-pymupdf-wasm-0.11.14.tgz -C /var/www/wasm/pymupdf --strip-components=1
|
||||
tar xzf bentopdf-gs-wasm-*.tgz -C /var/www/wasm/gs --strip-components=1
|
||||
tar xzf coherentpdf-*.tgz -C /var/www/wasm/cpdf --strip-components=1
|
||||
TEMP_TESS=$(mktemp -d)
|
||||
tar xzf tesseract.js-7.0.0.tgz -C "$TEMP_TESS"
|
||||
cp "$TEMP_TESS/package/dist/worker.min.js" /var/www/wasm/ocr/worker.min.js
|
||||
rm -rf "$TEMP_TESS"
|
||||
tar xzf tesseract.js-core-7.0.0.tgz -C /var/www/wasm/ocr/core --strip-components=1
|
||||
cp ./tesseract-langdata/*.traineddata.gz /var/www/wasm/ocr/lang-data/
|
||||
cp ./ocr-fonts/* /var/www/wasm/ocr/fonts/
|
||||
|
||||
# Run BentoPDF
|
||||
docker run -d -p 3000:8080 --restart unless-stopped bentopdf
|
||||
```
|
||||
|
||||
Verwenden Sie für `--ocr-languages` die Codes, die `bash scripts/prepare-airgap.sh --list-ocr-languages` ausgibt, oder suchen Sie per Name mit `bash scripts/prepare-airgap.sh --search-ocr-language <term>`. Wenn Sie mit einer eingeschränkten OCR-Teilmenge bauen, übergib dieselben Codes an `VITE_TESSERACT_AVAILABLE_LANGUAGES`, damit die App nur gebündelte Sprachen anzeigt. Für eine vollständige Offline-OCR-Ausgabe hosten Sie zusätzlich das mitgelieferte Verzeichnis `ocr-fonts/` und lassen Sie `VITE_OCR_FONT_BASE_URL` darauf zeigen.
|
||||
|
||||
Setzen Sie eine Variable auf einen leeren String, um das jeweilige Modul zu deaktivieren (Nutzer müssen es dann manuell über die Erweiterten Einstellungen konfigurieren).
|
||||
|
||||
## Eigener Port
|
||||
|
||||
Standardmäßig lauscht BentoPDF im Container auf Port `8080`. Um das zu ändern, setzen Sie zur Laufzeit die Umgebungsvariable `PORT`:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
| Variable | Beschreibung | Standard |
|
||||
| -------- | ------------------------------ | -------- |
|
||||
| `PORT` | Nginx-Listen-Port im Container | `8080` |
|
||||
|
||||
Das funktioniert sowohl mit dem Standard- als auch mit dem Nonroot-Dockerfile.
|
||||
|
||||
## Eigene Benutzer-ID (PUID/PGID)
|
||||
|
||||
Für Umgebungen, die einen bestimmten Nicht-Root-Benutzer erfordern (NAS-Geräte, Kubernetes mit Security-Contexts, organisatorische Vorgaben), bietet BentoPDF ein separates Dockerfile mit PUID/PGID-Unterstützung im LSIO-Stil.
|
||||
|
||||
### Bauen und ausführen
|
||||
|
||||
```bash
|
||||
# Build the non-root image
|
||||
docker build -f Dockerfile.nonroot -t bentopdf-nonroot .
|
||||
|
||||
# Run with custom UID/GID
|
||||
docker run -d \
|
||||
--name bentopdf \
|
||||
-p 3000:8080 \
|
||||
-e PUID=1000 \
|
||||
-e PGID=1000 \
|
||||
--restart unless-stopped \
|
||||
bentopdf-nonroot
|
||||
```
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
| Variable | Beschreibung | Standard |
|
||||
| -------------- | -------------------------------------- | -------- |
|
||||
| `PUID` | Benutzer-ID, unter der ausgeführt wird | `1000` |
|
||||
| `PGID` | Gruppen-ID, unter der ausgeführt wird | `1000` |
|
||||
| `PORT` | Nginx-Listen-Port | `8080` |
|
||||
| `DISABLE_IPV6` | IPv6-Listener deaktivieren | `false` |
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.nonroot
|
||||
container_name: bentopdf
|
||||
ports:
|
||||
- '3000:8080'
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### So funktioniert es
|
||||
|
||||
Der Container startet als root, legt einen Benutzer mit der angegebenen PUID/PGID an, passt die Besitzrechte aller beschreibbaren Verzeichnisse an und gibt dann mit `su-exec` die Rechte ab. Der nginx-Prozess läuft vollständig unter dem angegebenen Benutzer.
|
||||
|
||||
> [!NOTE]
|
||||
> Das Standard-`Dockerfile` verwendet `nginx-unprivileged` (UID 101) und wird für die meisten Bereitstellungen empfohlen. Nutzen Sie `Dockerfile.nonroot` nur, wenn Sie eine bestimmte UID/GID brauchen.
|
||||
|
||||
> [!WARNING]
|
||||
> PUID/PGID dürfen nicht `0` (root) sein. Der Entrypoint prüft die Eingaben und beendet sich bei ungültigen Werten mit einem Fehler.
|
||||
|
||||
## Mit Traefik (Reverse-Proxy)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.10
|
||||
command:
|
||||
- '--providers.docker=true'
|
||||
- '--entrypoints.web.address=:80'
|
||||
- '--entrypoints.websecure.address=:443'
|
||||
- '--certificatesresolvers.letsencrypt.acme.email=you@example.com'
|
||||
- '--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json'
|
||||
- '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./letsencrypt:/letsencrypt
|
||||
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
labels:
|
||||
- 'traefik.enable=true'
|
||||
- 'traefik.http.routers.bentopdf.rule=Host(`pdf.example.com`)'
|
||||
- 'traefik.http.routers.bentopdf.entrypoints=websecure'
|
||||
- 'traefik.http.routers.bentopdf.tls.certresolver=letsencrypt'
|
||||
- 'traefik.http.services.bentopdf.loadbalancer.server.port=8080'
|
||||
# Required headers for SharedArrayBuffer (LibreOffice WASM)
|
||||
- 'traefik.http.routers.bentopdf.middlewares=bentopdf-headers'
|
||||
- 'traefik.http.middlewares.bentopdf-headers.headers.customresponseheaders.Cross-Origin-Opener-Policy=same-origin'
|
||||
- 'traefik.http.middlewares.bentopdf-headers.headers.customresponseheaders.Cross-Origin-Embedder-Policy=require-corp'
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
## Mit Caddy (Reverse-Proxy)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
```
|
||||
|
||||
Caddyfile:
|
||||
|
||||
```
|
||||
pdf.example.com {
|
||||
reverse_proxy bentopdf:8080
|
||||
header Cross-Origin-Opener-Policy "same-origin"
|
||||
header Cross-Origin-Embedder-Policy "require-corp"
|
||||
}
|
||||
```
|
||||
|
||||
## Ressourcenlimits
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
```
|
||||
|
||||
## Podman Quadlet (systemd-Integration)
|
||||
|
||||
[Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) ermöglicht es, Podman-Container als systemd-Dienste auszuführen. Das ist ideal für Produktionsbereitstellungen auf Linux-Systemen.
|
||||
|
||||
### Grundlegende Quadlet-Einrichtung
|
||||
|
||||
Erstellen Sie eine Container-Unit-Datei unter `~/.config/containers/systemd/bentopdf.container` (Benutzer) oder `/etc/containers/systemd/bentopdf.container` (System):
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=BentoPDF - Privacy-first PDF toolkit
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
### Aktivieren und starten
|
||||
|
||||
```bash
|
||||
# Reload systemd to detect new unit
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Start the service
|
||||
systemctl --user start bentopdf
|
||||
|
||||
# Enable on boot
|
||||
systemctl --user enable bentopdf
|
||||
|
||||
# Check status
|
||||
systemctl --user status bentopdf
|
||||
|
||||
# View logs
|
||||
journalctl --user -u bentopdf -f
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Für eine systemweite Bereitstellung nutzen Sie `systemctl` ohne das Flag `--user` und legen Sie die Datei in `/etc/containers/systemd/` ab.
|
||||
|
||||
### Quadlet mit Health-Check
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=BentoPDF with health monitoring
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
AutoUpdate=registry
|
||||
HealthCmd=wget --spider -q http://localhost:8080 || exit 1
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=3
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
### Automatisches Update mit Quadlet
|
||||
|
||||
Podman kann Container automatisch aktualisieren, wenn neue Images verfügbar sind:
|
||||
|
||||
```bash
|
||||
# Enable auto-update timer
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
|
||||
# Check for updates manually
|
||||
podman auto-update
|
||||
|
||||
# Dry run (check without updating)
|
||||
podman auto-update --dry-run
|
||||
```
|
||||
|
||||
### Quadlet-Netzwerkkonfiguration
|
||||
|
||||
Für eine eigene Netzwerkkonfiguration erstellen Sie eine Netzwerkdatei `bentopdf.network`:
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Subnet=10.89.0.0/24
|
||||
Gateway=10.89.0.1
|
||||
```
|
||||
|
||||
Verweisen Sie dann in Ihrer Container-Datei darauf:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
Network=bentopdf.network
|
||||
```
|
||||
|
||||
## Aktualisieren
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Recreate container
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Auf Hostinger bereitstellen
|
||||
|
||||
[Hostinger](https://hostinger.com) ist ein beliebter Shared-Hosting-Anbieter. Diese Anleitung zeigt, wie Sie BentoPDF auf dem Shared Hosting von Hostinger bereitstellen.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Hostinger-Hosting-Tarif mit Zugriff auf den Dateimanager
|
||||
- Lokal installiertes Node.js zum Bauen
|
||||
|
||||
## Schritt 1: Projekt bauen
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Die gebauten Dateien liegen im Ordner `dist`.
|
||||
|
||||
## Schritt 2: Auf Hostinger hochladen
|
||||
|
||||
### Bereitstellung auf der Hauptdomain
|
||||
|
||||
1. Melden Sie sich bei Ihrem Hostinger-Konto an
|
||||
2. Gehen Sie zu **File Manager** → **public_html**
|
||||
3. **Löschen Sie** vorhandene Dateien (bei Bedarf vorher sichern)
|
||||
4. **Laden Sie** den gesamten Inhalt Ihres lokalen Ordners `dist` nach `public_html` **hoch**
|
||||
|
||||
### Bereitstellung in einem Unterverzeichnis
|
||||
|
||||
Wenn Sie in einem Unterverzeichnis bereitstellen (z. B. `yourdomain.com/pdf-tools/`):
|
||||
|
||||
1. Mit der richtigen Basis-URL bauen:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
2. Den Ordner in Hostinger erstellen:
|
||||
- Gehen Sie zu **File Manager** → **public_html**
|
||||
- Erstellen Sie einen neuen Ordner: `pdf-tools`
|
||||
3. Laden Sie den gesamten Inhalt von `dist` nach `public_html/pdf-tools/` hoch
|
||||
|
||||
## Schritt 3: .htaccess-Datei erstellen
|
||||
|
||||
Erstellen Sie eine `.htaccess`-Datei im Stammverzeichnis Ihres Bereitstellungsordners (`public_html` oder `public_html/pdf-tools/`):
|
||||
|
||||
::: warning Wichtig
|
||||
Bei der Bereitstellung in einem Unterverzeichnis änderen Sie `RewriteBase /` in `RewriteBase /pdf-tools/` (oder in den Namen Ihres Ordners).
|
||||
:::
|
||||
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# ============================================
|
||||
# 1. SECURITY HEADERS (CRITICAL FOR WASM)
|
||||
# ============================================
|
||||
<IfModule mod_headers.c>
|
||||
Header always set X-Frame-Options "SAMEORIGIN"
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
Header always set X-XSS-Protection "1; mode=block"
|
||||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
|
||||
|
||||
# REQUIRED for soffice.js (SharedArrayBuffer)
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
</IfModule>
|
||||
|
||||
# ============================================
|
||||
# 2. BROWSER CACHING
|
||||
# ============================================
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/gif "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
ExpiresByType font/woff2 "access plus 1 year"
|
||||
ExpiresByType font/woff "access plus 1 year"
|
||||
ExpiresByType font/ttf "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
ExpiresByType application/wasm "access plus 1 year"
|
||||
ExpiresByType application/gzip "access plus 1 year"
|
||||
ExpiresByType text/html "access plus 0 seconds"
|
||||
</IfModule>
|
||||
|
||||
# ============================================
|
||||
# 3. COMPRESSION (STANDARD)
|
||||
# ============================================
|
||||
# Prevent server from double-compressing files
|
||||
SetEnvIfNoCase Request_URI "\.gz$" no-gzip
|
||||
SetEnvIfNoCase Request_URI "\.br$" no-gzip
|
||||
SetEnvIfNoCase Request_URI "\.wasm$" no-gzip
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/plain
|
||||
AddOutputFilterByType DEFLATE text/html
|
||||
AddOutputFilterByType DEFLATE text/xml
|
||||
AddOutputFilterByType DEFLATE text/css
|
||||
AddOutputFilterByType DEFLATE application/xml
|
||||
AddOutputFilterByType DEFLATE application/xhtml+xml
|
||||
AddOutputFilterByType DEFLATE application/rss+xml
|
||||
AddOutputFilterByType DEFLATE application/javascript
|
||||
AddOutputFilterByType DEFLATE application/x-javascript
|
||||
AddOutputFilterByType DEFLATE application/json
|
||||
AddOutputFilterByType DEFLATE image/svg+xml
|
||||
AddOutputFilterByType DEFLATE font/woff
|
||||
AddOutputFilterByType DEFLATE font/ttf
|
||||
</IfModule>
|
||||
|
||||
# ============================================
|
||||
# 4. MIME TYPES & SPECIAL FILE HANDLING
|
||||
# ============================================
|
||||
AddType application/javascript .js .mjs
|
||||
AddType application/wasm .wasm
|
||||
AddType font/woff2 .woff2
|
||||
AddType font/woff .woff
|
||||
AddType image/webp .webp
|
||||
|
||||
# Handle soffice.wasm.gz correctly
|
||||
<FilesMatch "soffice\.wasm\.gz$">
|
||||
ForceType application/wasm
|
||||
Header set Content-Encoding "gzip"
|
||||
Header set Cross-Origin-Resource-Policy "cross-origin"
|
||||
Header append Vary Accept-Encoding
|
||||
</FilesMatch>
|
||||
|
||||
# Handle data.gz
|
||||
<FilesMatch "soffice\.data\.gz$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Encoding "gzip"
|
||||
Header append Vary Accept-Encoding
|
||||
</FilesMatch>
|
||||
|
||||
# ============================================
|
||||
# 5. REDIRECTS & ROUTING
|
||||
# ============================================
|
||||
# Canonical WWW (update domain as needed)
|
||||
RewriteCond %{HTTP_HOST} ^bentopdf\.com [NC]
|
||||
RewriteRule ^(.*)$ https://www.bentopdf.com/$1 [L,R=301]
|
||||
|
||||
# Force HTTPS
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Remove trailing slash (except for language root directories)
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !^/(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/$
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [R=301,L]
|
||||
|
||||
# Existing files/dirs - serve directly
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# ============================================
|
||||
# 5.1. LANGUAGE ROUTES
|
||||
# ============================================
|
||||
# Supported languages: de, es, zh, zh-TW, vi, it, id, tr, fr, pt
|
||||
# English has no prefix - served from root
|
||||
|
||||
# English prefix redirects to root (for SEO consistency)
|
||||
RewriteRule ^en/?$ / [R=301,L]
|
||||
RewriteRule ^en/(.+)$ /$1 [R=301,L]
|
||||
|
||||
# Language prefix root (e.g., /de/ -> /de/index.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /$1/index.html [L]
|
||||
|
||||
# Language prefix with path (e.g., /de/merge-pdf -> /de/merge-pdf.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /$1/$2.html [L]
|
||||
|
||||
# ============================================
|
||||
# 5.5. DOCS ROUTING (VitePress)
|
||||
# ============================================
|
||||
RewriteCond %{REQUEST_URI} ^/docs
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME}\.html -f
|
||||
RewriteRule ^(.*)$ $1.html [L]
|
||||
|
||||
# ============================================
|
||||
# 6. ADD .HTML EXTENSION IF FILE EXISTS (ROOT LEVEL ONLY)
|
||||
# ============================================
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
|
||||
# ============================================
|
||||
# 7. ERROR PAGES
|
||||
# ============================================
|
||||
ErrorDocument 404 /404.html
|
||||
```
|
||||
|
||||
## Beispiel für .htaccess im Unterverzeichnis
|
||||
|
||||
Für `yourdomain.com/pdf-tools/` passen Sie diese Zeilen an:
|
||||
|
||||
```apache
|
||||
RewriteBase /pdf-tools/
|
||||
|
||||
# ... (same content as above, but update paths) ...
|
||||
|
||||
# Language prefix root
|
||||
RewriteCond %{DOCUMENT_ROOT}/pdf-tools/$1/index.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /pdf-tools/$1/index.html [L]
|
||||
|
||||
# Language prefix with path
|
||||
RewriteCond %{DOCUMENT_ROOT}/pdf-tools/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /pdf-tools/$1/$2.html [L]
|
||||
|
||||
# Root level .html extension
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
|
||||
ErrorDocument 404 /pdf-tools/404.html
|
||||
```
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### WASM-Dateien werden nicht geladen
|
||||
|
||||
Stellen Sie sicher, dass die MIME-Typen in der `.htaccess` korrekt gesetzt sind:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### LibreOffice-Werkzeuge funktionieren nicht
|
||||
|
||||
Die Sicherheits-Header sind für SharedArrayBuffer entscheidend:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
```
|
||||
|
||||
Werden die Header nicht angewendet, bitte den Hostinger-Support, `mod_headers` zu aktivieren.
|
||||
|
||||
### 404-Fehler beim Neuladen der Seite
|
||||
|
||||
Stellen Sie sicher, dass die Regel für die Endung `.html` und die Sprachrouten korrekt konfiguriert sind. BentoPDF nutzt statische HTML-Dateien, kein SPA-Routing:
|
||||
|
||||
```apache
|
||||
# Language routes serve actual files from language directories
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /$1/$2.html [L]
|
||||
|
||||
# Root level pages
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
```
|
||||
|
||||
### Limits für Datei-Uploads
|
||||
|
||||
Hostinger kann Dateigrößenlimits haben. Laden Sie WASM-Dateien separat hoch, wenn der Massen-Upload fehlschlägt.
|
||||
@@ -0,0 +1,538 @@
|
||||
# Self-Hosting-Leitfaden
|
||||
|
||||
BentoPDF kann auf Ihrer eigenen Infrastruktur selbst gehostet werden. Dieser Leitfaden behandelt verschiedene Bereitstellungsoptionen.
|
||||
|
||||
## Schnellstart mit Docker / Podman
|
||||
|
||||
Der schnellste Weg, BentoPDF selbst zu hosten:
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF gibt es in zwei Builds:
|
||||
>
|
||||
> - **Self-Hosted-Build** (`ghcr.io/alam00000/bentopdf-simple:latest`). Jedes PDF-Werkzeug, **ohne** das Marketing-Drumherum (kein Hero, keine FAQ, keine Testimonials, kein Footer). Nutzen Sie ihn für interne Bereitstellungen in Teams und Organisationen. Es ist **keine** funktionsreduzierte "Lite"-Version.
|
||||
> - **Commercial-Build** (`ghcr.io/alam00000/bentopdf:latest`). Die vollständige Marketing-Website, die von bentopdf.com selbst und von Inhabern einer kommerziellen Lizenz mit öffentlich zugänglichen Bereitstellungen verwendet wird.
|
||||
>
|
||||
> Im Zweifel: Ziehen Sie den Self-Hosted-Build.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Die Konvertierung von Office-Dateien erfordert `SharedArrayBuffer`. Die App muss dafür sowohl Cross-Origin-isoliert sein als auch aus einem sicheren Kontext ausgeliefert werden. Das offizielle Image sendet bereits die erforderlichen COOP/COEP-Header, aber Browser deaktivieren `SharedArrayBuffer` bei reinen HTTP-Ursprüngen im lokalen Netzwerk wie `http://192.168.x.x` trotzdem.
|
||||
>
|
||||
> Verwenden Sie `http://localhost` nur für Tests auf demselben Gerät. Wenn Nutzende BentoPDF über eine LAN-IP oder einen Hostnamen aufrufen, beenden Sie die Verbindung mit HTTPS.
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker run -d -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
|
||||
# Podman
|
||||
podman run -d -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
Oder mit Docker Compose / Podman Compose:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ports:
|
||||
- '3000:8080'
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Podman Quadlet (Linux Systemd)
|
||||
|
||||
Betreiben Sie BentoPDF als systemd-Dienst. Erstellen Sie `~/.config/containers/systemd/bentopdf.container`:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now bentopdf
|
||||
```
|
||||
|
||||
Die vollständige Quadlet-Dokumentation finden Sie im [Docker-Bereitstellungsleitfaden](/de/self-hosting/docker).
|
||||
|
||||
## Aus dem Quellcode bauen
|
||||
|
||||
```bash
|
||||
# Clone and build
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# The built files are in the `dist` folder
|
||||
```
|
||||
|
||||
## Konfigurationsoptionen
|
||||
|
||||
### Self-Hosted-Build (Simple Mode)
|
||||
|
||||
Der Self-Hosted-Build (das Image `bentopdf-simple`, auch Simple Mode genannt) ist **funktional identisch** mit dem Commercial-Build: Jedes PDF-Werkzeug ist vorhanden und verhält sich gleich. Er blendet lediglich das Marketing-Drumherum aus, das nur auf bentopdf.com selbst oder in einer kommerziellen, öffentlich zugänglichen Bereitstellung sinnvoll ist. **Es ist keine funktionsreduzierte oder "Lite"-Version.**
|
||||
|
||||
**Was der Self-Hosted-Build ausblendet** (nur kosmetisch, keine PDF-Funktionen werden entfernt):
|
||||
|
||||
- Navigationsleiste, Hero-Bereich, Funktionsbereich, FAQ, Testimonials, Footer
|
||||
- Setzt den Seitentitel auf "PDF-Werkzeuge"
|
||||
|
||||
**Was der Self-Hosted-Build behält** (alles, was tatsächlich PDF-Arbeit leistet):
|
||||
|
||||
- Jedes PDF-Werkzeug (Zusammenführen, Teilen, Bearbeiten, Signieren, OCR, Office-Konvertierung usw.)
|
||||
- Unterstützung für eigenes Branding, alle Build-Time- und Runtime-Konfigurationen
|
||||
|
||||
Der Commercial-Build (`ghcr.io/alam00000/bentopdf:latest`) betreibt bentopdf.com selbst und wird von Inhabern einer kommerziellen Lizenz für öffentlich zugängliche Bereitstellungen verwendet. Er ergänzt Hero, FAQ, Testimonials und Footer, die bei einem internen Werkzeug keinen Sinn ergeben würden.
|
||||
|
||||
Wenn Sie BentoPDF für Ihr Team, Ihre Organisation oder als internes Werkzeug selbst hosten, ziehen Sie den Self-Hosted-Build:
|
||||
|
||||
```bash
|
||||
# Use the pre-built image (recommended)
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
|
||||
# Or build it yourself
|
||||
SIMPLE_MODE=true npm run build
|
||||
```
|
||||
|
||||
Alle Details finden Sie in [SIMPLE_MODE.md](https://github.com/alam00000/bentopdf/blob/main/SIMPLE_MODE.md).
|
||||
|
||||
### Commercial-Build (öffentlich zugängliche Bereitstellungen mit eigener Marke)
|
||||
|
||||
Der Commercial-Build (das Image `bentopdf`, ohne Suffix `-simple`) betreibt bentopdf.com selbst. Er enthält die vollständige Marketing-Website (Hero, Funktionen, FAQ, Testimonials, Footer) zusätzlich zu jedem PDF-Werkzeug. Verwenden Sie diesen Build, wenn Sie **BentoPDF als öffentlich zugänglichen PDF-Dienst unter Ihrer eigenen Marke bereitstellen** möchten, zum Beispiel:
|
||||
|
||||
- Sie betreiben BentoPDF als gehostetes SaaS für Endnutzende auf Ihrer eigenen Domain
|
||||
- Sie möchten das Landingpage-Erlebnis (Marketing-Bereiche + Werkzeuge), nicht nur die reine Werkzeugoberfläche
|
||||
- Sie sind Inhaber einer kommerziellen Lizenz und betten BentoPDF in ein kommerzielles Produkt ein
|
||||
|
||||
**Unverändert ausführen** (mit BentoPDF-Branding, nützlich, um den Build auszuprobieren):
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
**Mit eigener Marke bauen** (der typische kommerzielle Workflow):
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg VITE_BRAND_NAME="AcmePDF" \
|
||||
--build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
--build-arg VITE_FOOTER_TEXT="© 2026 Acme Corp. All rights reserved." \
|
||||
-t acmepdf .
|
||||
|
||||
docker run -p 3000:8080 acmepdf
|
||||
```
|
||||
|
||||
Alle anderen Build-Time-Optionen (`BASE_URL`, `VITE_DEFAULT_LANGUAGE`, `DISABLE_TOOLS`, WASM-URL-Überschreibungen usw.) funktionieren genauso wie beim Self-Hosted-Build.
|
||||
|
||||
::: warning Lizenzierung
|
||||
Der Commercial-Build darf unter einer der beiden Lizenzoptionen von BentoPDF betrieben werden:
|
||||
|
||||
- **AGPL-3.0** (kostenlos): erlaubt, wenn Ihre Bereitstellung ihren vollständigen Quellcode unter der AGPL veröffentlicht, einschließlich aller Branding-Änderungen und der umgebenden Geschäftslogik.
|
||||
- **Kommerzielle Lizenz** (79 $ lebenslang): erforderlich für Closed-Source-/proprietäre Bereitstellungen, bei denen Sie Ihren Branding-Fork oder Ihren Geschäftscode nicht als Open Source veröffentlichen.
|
||||
|
||||
Den vollständigen Vergleich finden Sie auf der [Lizenzierungsseite](https://www.bentopdf.com/licensing). AGPL-lizenzierte WASM-Module (PyMuPDF, Ghostscript, CoherentPDF) werden zur Laufzeit von einem CDN geladen, gelangen also nicht in Ihr Image und ändern nichts an Ihrer Lizenzlage.
|
||||
:::
|
||||
|
||||
### Base-URL
|
||||
|
||||
In einem Unterverzeichnis bereitstellen:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
### Eigenes Branding
|
||||
|
||||
Ersetzen Sie das Standard-Logo, den Namen und den Footer-Text von BentoPDF zur Build-Zeit durch Ihre eigenen:
|
||||
|
||||
| Variable | Beschreibung | Standard |
|
||||
| ------------------ | ------------------------------- | --------------------------------------- |
|
||||
| `VITE_BRAND_NAME` | Markenname in Header und Footer | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logopfad relativ zu `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Eigener Footer-/Copyright-Text | `© 2026 BentoPDF. All rights reserved.` |
|
||||
|
||||
```bash
|
||||
# Place your logo in public/, then build
|
||||
VITE_BRAND_NAME="AcmePDF" \
|
||||
VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
VITE_FOOTER_TEXT="© 2026 Acme Corp. Internal use only." \
|
||||
npm run build
|
||||
```
|
||||
|
||||
Oder über Docker:
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg VITE_BRAND_NAME="AcmePDF" \
|
||||
--build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
--build-arg VITE_FOOTER_TEXT="© 2026 Acme Corp. Internal use only." \
|
||||
-t acmepdf .
|
||||
```
|
||||
|
||||
Das Branding funktioniert sowohl im vollständigen Modus als auch im Simple Mode und lässt sich mit allen anderen Build-Time-Optionen kombinieren (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Bestimmte Werkzeuge deaktivieren
|
||||
|
||||
Blenden Sie einzelne Werkzeuge aus Compliance- oder Sicherheitsgründen aus. Deaktivierte Werkzeuge werden auf der Startseite, in der Suche, in den Tastenkürzeln, im Workflow-Builder und beim direkten URL-Zugriff entfernt.
|
||||
|
||||
Werkzeug-IDs entsprechen der Seiten-URL ohne `.html`: Öffnen Sie ein beliebiges Werkzeug und sehen Sie sich die URL an (z. B. `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Build-Time** (in das Bundle eingebaut):
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**Runtime** (ohne Neubau: eine `config.json` einbinden):
|
||||
|
||||
```json
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -d -p 3000:8080 \
|
||||
-v ./config.json:/usr/share/nginx/html/config.json:ro \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
Beide Methoden lassen sich kombinieren, die Listen werden zusammengeführt.
|
||||
|
||||
Sie können außerdem einzelne Funktionen im PDF-Editor (z. B. Schwärzen) deaktivieren, ohne das gesamte Werkzeug zu deaktivieren, indem Sie `editorDisabledCategories` zur `config.json` hinzufügen. Die vollständige Liste der Kategorien finden Sie im [Docker-Leitfaden](/de/self-hosting/docker#editor-funktionen-deaktivieren).
|
||||
|
||||
## Bereitstellungsleitfäden
|
||||
|
||||
Wählen Sie Ihre Plattform:
|
||||
|
||||
- [Vercel](/de/self-hosting/vercel)
|
||||
- [Netlify](/de/self-hosting/netlify)
|
||||
- [Cloudflare Pages](/de/self-hosting/cloudflare)
|
||||
- [AWS S3 + CloudFront](/de/self-hosting/aws)
|
||||
- [Hostinger](/de/self-hosting/hostinger)
|
||||
- [Nginx](/de/self-hosting/nginx)
|
||||
- [Apache](/de/self-hosting/apache)
|
||||
- [Docker](/de/self-hosting/docker)
|
||||
- [Kubernetes](/de/self-hosting/kubernetes)
|
||||
- [CORS-Proxy](/de/self-hosting/cors-proxy) - Erforderlich für digitale Signaturen
|
||||
|
||||
## Häufige Probleme
|
||||
|
||||
### Word / ODT / Excel / PowerPoint zu PDF hängt (SharedArrayBuffer nicht verfügbar)
|
||||
|
||||
**Symptom**: LibreOffice-basierte Dokumentkonvertierungen (Word, ODT, Excel, PowerPoint zu PDF) hängen bei ca. 55 % oder starten nicht. Die Browserkonsole zeigt möglicherweise:
|
||||
|
||||
> `ReferenceError: SharedArrayBuffer is not defined`
|
||||
|
||||
…oder `window.crossOriginIsolated` meldet `false`, oder die WASM-Kompilierung meldet `expected magic word 00 61 73 6d, found 1f 8b 08 08`.
|
||||
|
||||
**Ursache**: LibreOffice-WASM benötigt `SharedArrayBuffer`, das der Browser nur aktiviert, wenn die Seite **Cross-Origin-isoliert** ist UND aus einem **sicheren Kontext** ausgeliefert wird. Dafür müssen zwei Dinge zutreffen:
|
||||
|
||||
1. Jede Antwort enthält beide Header:
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
2. Die Seite wird über `https://...` oder `http://localhost` ausgeliefert. Reines HTTP auf einer LAN-IP (z. B. `http://192.168.x.x`) zählt NICHT als sicher, dort deaktivieren Browser `SharedArrayBuffer`.
|
||||
|
||||
Die Abweichung `00 61 73 6d / 1f 8b 08 08` ist ein separates Teilsymptom: Den vorkomprimierten Dateien `.wasm.gz` / `.data.gz` fehlt der Response-Header `Content-Encoding: gzip`, sodass der Browser rohe gzip-Bytes statt dekomprimiertem WASM erhält.
|
||||
|
||||
**Lösung**: Die genaue Konfiguration finden Sie im Bereitstellungsleitfaden Ihrer Plattform:
|
||||
|
||||
- [Nginx →](/de/self-hosting/nginx#signieren-oder-formularausfullung-zeigt-einen-leeren-viewer-mjs-mime-fehler)
|
||||
- [Apache →](/de/self-hosting/apache#signieren-oder-formularausfullung-zeigt-einen-leeren-viewer-mjs-mime-fehler)
|
||||
- [AWS S3 + CloudFront →](/de/self-hosting/aws#schritt-3b-response-headers-policy-erforderlich-fur-libreoffice-wasm)
|
||||
- [Cloudflare Pages →](/de/self-hosting/cloudflare#konfigurationsdatei) (`_headers`-Datei)
|
||||
- [Netlify →](/de/self-hosting/netlify#word-odt-excel-zu-pdf-bleibt-bei-55-hangen) (`netlify.toml`)
|
||||
- [Vercel →](/de/self-hosting/vercel#word-odt-excel-zu-pdf-funktioniert-nicht) (`vercel.json`)
|
||||
- [Hostinger →](/de/self-hosting/hostinger#libreoffice-werkzeuge-funktionieren-nicht) (`.htaccess`)
|
||||
- [Kubernetes →](/de/self-hosting/kubernetes#mjs-mime-type-fehler-pdf-unterschreiben-pdf-formular-ausfullen-leerer-iframe)
|
||||
- **Docker**: wird von der mitgelieferten Nginx-Konfiguration automatisch erledigt, keine Aktion nötig.
|
||||
|
||||
**Überprüfen**: Öffnen Sie auf einer beliebigen BentoPDF-Seite die DevTools-Konsole und führen Sie aus:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Wenn die Seite über HTTPS oder `http://localhost` läuft UND beide COEP/COOP-Header in jeder Antwort vorhanden sind, bestehen beide Prüfungen. Wenn Sie auf `http://192.168.x.x` oder einem anderen nicht-Loopback-HTTP-Ursprung sind, beenden Sie die Verbindung mit HTTPS. Es gibt keine Lösung allein über Header.
|
||||
|
||||
### `.mjs`-Dateien werden als `application/octet-stream` ausgeliefert
|
||||
|
||||
**Symptom**: PDF unterschreiben / PDF-Formular ausfüllen / bestimmte andere Werkzeuge zeigen einen leeren Viewer oder laden nicht. Die Browserkonsole meldet:
|
||||
|
||||
> `Failed to load module script: The server responded with a non-JavaScript MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.`
|
||||
|
||||
**Ursache**: Ihr Webserver oder Reverse-Proxy hat keine MIME-Type-Zuordnung für `.mjs`-Dateien (der mitgelieferte PDF-Viewer enthält ES-Module mit dieser Endung). Viele Standard-Serverkonfigurationen verwenden für unbekannte Endungen `application/octet-stream`, was Browser als ES-Module nicht ausführen.
|
||||
|
||||
**Lösung**: Den genauen Ausschnitt finden Sie im Bereitstellungsleitfaden Ihrer Plattform:
|
||||
|
||||
- [Nginx →](/de/self-hosting/nginx#signieren-oder-formularausfullung-zeigt-einen-leeren-viewer-mjs-mime-fehler)
|
||||
- [Apache →](/de/self-hosting/apache#signieren-oder-formularausfullung-zeigt-einen-leeren-viewer-mjs-mime-fehler)
|
||||
- [AWS S3 + CloudFront →](/de/self-hosting/aws#schritt-2-bauen-und-hochladen) (siehe `aws s3 cp ... --include "*.mjs"`)
|
||||
- [Kubernetes →](/de/self-hosting/kubernetes#mjs-mime-type-fehler-pdf-unterschreiben-pdf-formular-ausfullen-leerer-iframe)
|
||||
- **Docker, Vercel, Netlify, Cloudflare Pages, Hostinger**: wird automatisch erledigt, keine Aktion nötig.
|
||||
|
||||
**Überprüfen**: Öffnen Sie die DevTools → Tab Network, suchen Sie die fehlgeschlagene `.mjs`-Anfrage und prüfen Sie den Response-Header `Content-Type`. Er sollte `application/javascript` lauten. Wenn er immer noch `application/octet-stream` lautet, bestimmt möglicherweise ein vorgelagerter Reverse-Proxy oder CDN den Typ neu. Prüfen Sie jede Schicht in Ihrer Auslieferungskette.
|
||||
|
||||
## WASM-Konfiguration (AGPL-Komponenten)
|
||||
|
||||
BentoPDF **bündelt keine** AGPL-lizenzierten Verarbeitungsbibliotheken in seinem Quellcode, **konfiguriert aber CDN-URLs vor**, sodass alle Funktionen sofort funktionieren, ohne manuelle Einrichtung.
|
||||
|
||||
::: tip Standardmäßig ohne Konfiguration
|
||||
Seit v2.0.0 sind WASM-Module so vorkonfiguriert, dass sie über Umgebungsvariablen vom jsDelivr-CDN geladen werden. Alle erweiterten Funktionen funktionieren sofort, ohne Konfiguration durch Nutzende.
|
||||
:::
|
||||
|
||||
| Komponente | Lizenz | Funktionen |
|
||||
| --------------- | -------- | ---------------------------------------------------------------------- |
|
||||
| **PyMuPDF** | AGPL-3.0 | EPUB-/MOBI-/FB2-/XPS-Konvertierung, Bildextraktion, Tabellenextraktion |
|
||||
| **Ghostscript** | AGPL-3.0 | PDF/A-Konvertierung, Komprimierung, Begradigung, Rasterung |
|
||||
| **CoherentPDF** | AGPL-3.0 | Inhaltsverzeichnis, Anhänge, PDF-Zusammenführung mit Lesezeichen |
|
||||
|
||||
### Standard-Umgebungsvariablen
|
||||
|
||||
Diese sind in `.env.production` gesetzt und in den Build eingebaut:
|
||||
|
||||
```bash
|
||||
VITE_WASM_PYMUPDF_URL=https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/
|
||||
VITE_WASM_GS_URL=https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/
|
||||
VITE_WASM_CPDF_URL=https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/
|
||||
VITE_TESSERACT_WORKER_URL=
|
||||
VITE_TESSERACT_CORE_URL=
|
||||
VITE_TESSERACT_LANG_URL=
|
||||
VITE_TESSERACT_AVAILABLE_LANGUAGES=
|
||||
VITE_OCR_FONT_BASE_URL=
|
||||
```
|
||||
|
||||
### WASM-URLs überschreiben
|
||||
|
||||
Sie können die Standardwerte zur Build-Zeit für eigene Bereitstellungen überschreiben:
|
||||
|
||||
```bash
|
||||
# Via Docker build args
|
||||
docker build \
|
||||
--build-arg VITE_WASM_PYMUPDF_URL=https://your-server.com/pymupdf/ \
|
||||
--build-arg VITE_WASM_GS_URL=https://your-server.com/gs/ \
|
||||
--build-arg VITE_WASM_CPDF_URL=https://your-server.com/cpdf/ \
|
||||
--build-arg VITE_TESSERACT_WORKER_URL=https://your-server.com/ocr/worker.min.js \
|
||||
--build-arg VITE_TESSERACT_CORE_URL=https://your-server.com/ocr/core \
|
||||
--build-arg VITE_TESSERACT_LANG_URL=https://your-server.com/ocr/lang-data \
|
||||
--build-arg VITE_TESSERACT_AVAILABLE_LANGUAGES=eng,deu \
|
||||
--build-arg VITE_OCR_FONT_BASE_URL=https://your-server.com/ocr/fonts \
|
||||
-t bentopdf .
|
||||
|
||||
# Or via .env.production before building from source
|
||||
VITE_WASM_PYMUPDF_URL=https://your-server.com/pymupdf/ npm run build
|
||||
```
|
||||
|
||||
Um ein Modul vollständig zu deaktivieren (manuelle Konfiguration durch Nutzende über die erweiterten Einstellungen erforderlich), setzen Sie seine Variable auf eine leere Zeichenfolge.
|
||||
|
||||
Für OCR lassen Sie entweder alle `VITE_TESSERACT_*`-Variablen leer und behalten die standardmäßigen Online-Assets, oder Sie setzen die Worker-/Core-/Sprach-URLs gemeinsam für selbst gehostetes/Offline-OCR. Wenn Sie nur bestimmte OCR-Sprachen bündeln, setzen Sie `VITE_TESSERACT_AVAILABLE_LANGUAGES` ebenfalls auf dieselben kommagetrennten Codes, damit die Oberfläche nur installierte Sprachen anbietet und nicht unterstützte Auswahlen mit einer aussagekräftigen Fehlermeldung fehlschlagen. Für eine vollständig offline erzeugte durchsuchbare PDF-Ausgabe setzen Sie außerdem `VITE_OCR_FONT_BASE_URL` auf das interne Verzeichnis, das die mitgelieferten OCR-Schriftarten bereitstellt.
|
||||
|
||||
Nutzende können diese Standardwerte jederzeit über die **erweiterten Einstellungen** in der Oberfläche überschreiben. Im Browser gespeicherte Überschreibungen haben Vorrang vor den Standardwerten der Umgebung.
|
||||
|
||||
### Air-Gapped- / Offline-Bereitstellung
|
||||
|
||||
Für Netzwerke ohne Internetzugang (Behörden, Gesundheitswesen, Finanzwesen usw.). Die WASM-URLs sind zur **Build-Zeit** in das JavaScript eingebaut, die eigentlichen WASM-Dateien werden zur Laufzeit vom **Browser der Nutzenden** heruntergeladen. Sie müssen also alles auf einem Rechner mit Internetzugang vorbereiten und dann in das isolierte Netzwerk übertragen.
|
||||
|
||||
#### Automatisiertes Skript (empfohlen)
|
||||
|
||||
Das mitgelieferte Skript `prepare-airgap.sh` automatisiert den gesamten Ablauf: Es lädt die WASM-Pakete herunter, baut das Docker-Image und erzeugt ein in sich geschlossenes Bundle mit einem Setup-Skript.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
|
||||
# Show supported OCR language codes (for --ocr-languages)
|
||||
bash scripts/prepare-airgap.sh --list-ocr-languages
|
||||
|
||||
# Search OCR language codes by name or abbreviation
|
||||
bash scripts/prepare-airgap.sh --search-ocr-language german
|
||||
|
||||
# Interactive mode — prompts for all options
|
||||
bash scripts/prepare-airgap.sh
|
||||
|
||||
# Or fully automated
|
||||
bash scripts/prepare-airgap.sh --wasm-base-url https://internal.example.com/wasm
|
||||
```
|
||||
|
||||
Dies erzeugt ein Bundle-Verzeichnis:
|
||||
|
||||
```
|
||||
bentopdf-airgap-bundle/
|
||||
bentopdf.tar # Docker image
|
||||
*.tgz # WASM packages (PyMuPDF, Ghostscript, CoherentPDF, Tesseract)
|
||||
tesseract-langdata/ # OCR traineddata files
|
||||
ocr-fonts/ # OCR text-layer font files
|
||||
setup.sh # Setup script for the air-gapped side
|
||||
README.md # Instructions
|
||||
```
|
||||
|
||||
Übertragen Sie das Bundle per USB, internem Artefakt-Repository oder auf einem anderen genehmigten Weg in das Air-Gapped-Netzwerk. Führen Sie dann das mitgelieferte Setup-Skript aus:
|
||||
|
||||
```bash
|
||||
cd bentopdf-airgap-bundle
|
||||
bash setup.sh
|
||||
```
|
||||
|
||||
Das Setup-Skript lädt das Docker-Image, entpackt die WASM-Dateien und startet optional den Container.
|
||||
|
||||
**Skriptoptionen:**
|
||||
|
||||
| Flag | Beschreibung | Standard |
|
||||
| ------------------------------ | ------------------------------------------------------------- | ------------------------------------------- |
|
||||
| `--wasm-base-url <url>` | Wo die WASMs intern gehostet werden | _(erforderlich, wird bei Fehlen abgefragt)_ |
|
||||
| `--image-name <name>` | Docker-Image-Tag | `bentopdf` |
|
||||
| `--output-dir <path>` | Ausgabeverzeichnis des Bundles | `./bentopdf-airgap-bundle` |
|
||||
| `--simple-mode` | Simple Mode aktivieren | aus |
|
||||
| `--base-url <path>` | Base-URL des Unterverzeichnisses (z. B. `/pdf/`) | `/` |
|
||||
| `--language <code>` | Standardsprache der Oberfläche (z. B. `fr`, `de`) | _(keine)_ |
|
||||
| `--brand-name <name>` | Eigener Markenname | _(keine)_ |
|
||||
| `--brand-logo <path>` | Logopfad relativ zu `public/` | _(keine)_ |
|
||||
| `--footer-text <text>` | Eigener Footer-Text | _(keine)_ |
|
||||
| `--ocr-languages <list>` | Kommagetrennte OCR-Sprachen zum Bündeln | `eng` |
|
||||
| `--list-ocr-languages` | Unterstützte OCR-Codes und Namen ausgeben, dann beenden | aus |
|
||||
| `--search-ocr-language <term>` | OCR-Codes nach Name oder Abkürzung suchen | aus |
|
||||
| `--dockerfile <path>` | Zu verwendendes Dockerfile | `Dockerfile` |
|
||||
| `--skip-docker` | Docker-Build und -Export überspringen | aus |
|
||||
| `--skip-wasm` | WASM-Download überspringen (vorhandene `.tgz`-Dateien nutzen) | aus |
|
||||
|
||||
Die interaktive Abfrage akzeptiert außerdem `list`, um die vollständige Liste der unterstützten Tesseract-Codes auszugeben, und `search <term>`, um Treffer wie `search german` oder `search chi` zu finden.
|
||||
|
||||
::: warning Same-Origin-Anforderung
|
||||
WASM-Dateien müssen vom **selben Ursprung** wie die BentoPDF-App ausgeliefert werden. Web Worker verwenden `importScripts()`, das keine Skripte von anderen Ursprüngen laden kann. Wenn BentoPDF beispielsweise unter `https://internal.example.com` läuft, sollte auch die WASM-Basis-URL `https://internal.example.com/wasm` lauten.
|
||||
:::
|
||||
|
||||
#### Manuelle Schritte
|
||||
|
||||
<details>
|
||||
<summary>Wenn Sie es lieber manuell ohne das Skript machen</summary>
|
||||
|
||||
**Schritt 1: WASM- und OCR-Pakete herunterladen** (auf einem Rechner mit Internetzugang)
|
||||
|
||||
```bash
|
||||
npm pack @bentopdf/pymupdf-wasm@0.11.14
|
||||
npm pack @bentopdf/gs-wasm
|
||||
npm pack coherentpdf
|
||||
npm pack tesseract.js@7.0.0
|
||||
npm pack tesseract.js-core@7.0.0
|
||||
mkdir -p tesseract-langdata
|
||||
curl -fsSL https://cdn.jsdelivr.net/npm/@tesseract.js-data/eng/4.0.0_best_int/eng.traineddata.gz -o tesseract-langdata/eng.traineddata.gz
|
||||
mkdir -p ocr-fonts
|
||||
curl -fsSL https://raw.githack.com/googlefonts/noto-fonts/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf -o ocr-fonts/NotoSans-Regular.ttf
|
||||
```
|
||||
|
||||
**Schritt 2: Das Docker-Image mit internen URLs bauen**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
|
||||
docker build \
|
||||
--build-arg VITE_WASM_PYMUPDF_URL=https://internal-server.example.com/wasm/pymupdf/ \
|
||||
--build-arg VITE_WASM_GS_URL=https://internal-server.example.com/wasm/gs/ \
|
||||
--build-arg VITE_WASM_CPDF_URL=https://internal-server.example.com/wasm/cpdf/ \
|
||||
--build-arg VITE_TESSERACT_WORKER_URL=https://internal-server.example.com/wasm/ocr/worker.min.js \
|
||||
--build-arg VITE_TESSERACT_CORE_URL=https://internal-server.example.com/wasm/ocr/core \
|
||||
--build-arg VITE_TESSERACT_LANG_URL=https://internal-server.example.com/wasm/ocr/lang-data \
|
||||
--build-arg VITE_OCR_FONT_BASE_URL=https://internal-server.example.com/wasm/ocr/fonts \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
**Schritt 3: Das Docker-Image exportieren**
|
||||
|
||||
```bash
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
```
|
||||
|
||||
**Schritt 4: In das Air-Gapped-Netzwerk übertragen**
|
||||
|
||||
Kopieren Sie per USB, internem Artefakt-Repository oder einem genehmigten Übertragungsweg:
|
||||
|
||||
- `bentopdf.tar`: das Docker-Image
|
||||
- Die fünf `.tgz`-WASM-/OCR-Pakete aus Schritt 1
|
||||
- Das Verzeichnis `tesseract-langdata/` aus Schritt 1
|
||||
- Das Verzeichnis `ocr-fonts/` aus Schritt 1
|
||||
|
||||
**Schritt 5: Im Air-Gapped-Netzwerk einrichten**
|
||||
|
||||
```bash
|
||||
# Load the Docker image
|
||||
docker load -i bentopdf.tar
|
||||
|
||||
# Extract WASM packages
|
||||
mkdir -p ./wasm/pymupdf ./wasm/gs ./wasm/cpdf ./wasm/ocr/core ./wasm/ocr/lang-data ./wasm/ocr/fonts
|
||||
tar xzf bentopdf-pymupdf-wasm-0.11.14.tgz -C ./wasm/pymupdf --strip-components=1
|
||||
tar xzf bentopdf-gs-wasm-*.tgz -C ./wasm/gs --strip-components=1
|
||||
tar xzf coherentpdf-*.tgz -C ./wasm/cpdf --strip-components=1
|
||||
TEMP_TESS=$(mktemp -d)
|
||||
tar xzf tesseract.js-7.0.0.tgz -C "$TEMP_TESS"
|
||||
cp "$TEMP_TESS/package/dist/worker.min.js" ./wasm/ocr/worker.min.js
|
||||
rm -rf "$TEMP_TESS"
|
||||
tar xzf tesseract.js-core-7.0.0.tgz -C ./wasm/ocr/core --strip-components=1
|
||||
cp ./tesseract-langdata/*.traineddata.gz ./wasm/ocr/lang-data/
|
||||
cp ./ocr-fonts/* ./wasm/ocr/fonts/
|
||||
|
||||
# Run BentoPDF
|
||||
docker run -d -p 3000:8080 --restart unless-stopped bentopdf
|
||||
```
|
||||
|
||||
Stellen Sie sicher, dass die Dateien unter den in Schritt 2 konfigurierten URLs erreichbar sind, einschließlich `.../ocr/worker.min.js`, `.../ocr/core`, `.../ocr/lang-data` und `.../ocr/fonts`.
|
||||
|
||||
</details>
|
||||
|
||||
::: info Aus dem Quellcode statt mit Docker bauen?
|
||||
Setzen Sie die Variablen in `.env.production`, bevor Sie `npm run build` ausführen:
|
||||
|
||||
```bash
|
||||
VITE_WASM_PYMUPDF_URL=https://internal-server.example.com/wasm/pymupdf/
|
||||
VITE_WASM_GS_URL=https://internal-server.example.com/wasm/gs/
|
||||
VITE_WASM_CPDF_URL=https://internal-server.example.com/wasm/cpdf/
|
||||
VITE_TESSERACT_WORKER_URL=https://internal-server.example.com/wasm/ocr/worker.min.js
|
||||
VITE_TESSERACT_CORE_URL=https://internal-server.example.com/wasm/ocr/core
|
||||
VITE_TESSERACT_LANG_URL=https://internal-server.example.com/wasm/ocr/lang-data
|
||||
VITE_OCR_FONT_BASE_URL=https://internal-server.example.com/wasm/ocr/fonts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Einen eigenen WASM-Proxy hosten
|
||||
|
||||
Wenn Sie AGPL-WASM-Dateien mit passenden CORS-Headern ausliefern müssen, können Sie einen einfachen Proxy bereitstellen. Eine Beispielimplementierung finden Sie im [Cloudflare-WASM-Proxy-Leitfaden](https://github.com/alam00000/bentopdf/blob/main/cloudflare/WASM-PROXY.md).
|
||||
|
||||
::: tip Warum getrennt?
|
||||
Diese Trennung sorgt für:
|
||||
|
||||
- Klare rechtliche Konformität für kommerzielle Nutzende
|
||||
- Den Kern von BentoPDF unter seiner Dual-Lizenz (AGPL-3.0 / Commercial)
|
||||
- WASM-Dateien, die zur Laufzeit geladen und nicht im Quellcode gebündelt werden
|
||||
:::
|
||||
|
||||
## Systemanforderungen
|
||||
|
||||
| Anforderung | Minimum |
|
||||
| ----------- | ---------------------------------- |
|
||||
| Speicher | ca. 100 MB (Kern ohne AGPL-Module) |
|
||||
| RAM | 512 MB |
|
||||
| CPU | Jeder moderne Prozessor |
|
||||
|
||||
::: tip
|
||||
BentoPDF ist eine statische Website, es werden keine Datenbank und kein Backend-Server benötigt!
|
||||
:::
|
||||
@@ -0,0 +1,211 @@
|
||||
# Mit Kubernetes bereitstellen
|
||||
|
||||
Kubernetes ist für eine statische Website vielleicht überdimensioniert, kann aber gut passen, wenn Sie ohnehin auf Helm + GitOps setzen.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Erforderliche Header für die Office-Dateikonvertierung**
|
||||
>
|
||||
> LibreOffice-basierte Werkzeuge (Word-, Excel-, PowerPoint-Konvertierung) benötigen diese HTTP-Header für die `SharedArrayBuffer`-Unterstützung:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Die offiziellen BentoPDF-Nginx-Images enthalten diese Header. In Kubernetes sind **Ingress-/Gateway-Controller ebenfalls Reverse-Proxys**. Stellen Sie daher sicher, dass diese Header erhalten bleiben (oder fügen Sie sie am Edge hinzu).
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Kubernetes-Cluster
|
||||
- Helm v3
|
||||
- Ein BentoPDF-Nginx-Image (z. B. `ghcr.io/alam00000/bentopdf:<tag>`), das auf **Port 8080** läuft
|
||||
|
||||
## Mit Helm bereitstellen
|
||||
|
||||
### Aus diesem Repo installieren (lokales Chart)
|
||||
|
||||
```bash
|
||||
kubectl create namespace bentopdf
|
||||
|
||||
helm upgrade --install bentopdf /path/to/bentopdf/chart \
|
||||
--namespace bentopdf \
|
||||
--set image.repository=ghcr.io/alam00000/bentopdf \
|
||||
--set image.tag=latest
|
||||
```
|
||||
|
||||
### Aus GHCR installieren (OCI-Chart)
|
||||
|
||||
Wenn das Chart als OCI-Artefakt in GHCR veröffentlicht ist:
|
||||
|
||||
```bash
|
||||
export GHCR_USERNAME="<github-org-or-user>"
|
||||
|
||||
helm upgrade --install bentopdf oci://ghcr.io/$GHCR_USERNAME/charts/bentopdf \
|
||||
--namespace bentopdf \
|
||||
--create-namespace \
|
||||
--version 0.1.0 \
|
||||
--set image.repository=ghcr.io/alam00000/bentopdf \
|
||||
--set image.tag=latest
|
||||
```
|
||||
|
||||
## Verfügbar machen
|
||||
|
||||
### Port-Forward (schneller Test)
|
||||
|
||||
```bash
|
||||
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
|
||||
```
|
||||
|
||||
### Ingress (optional)
|
||||
|
||||
Aktivieren Sie Ingress (Beispiel für nginx-ingress):
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: pdf.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Gateway API (optional)
|
||||
|
||||
Dieses Chart unterstützt Gateway API `Gateway` + `HTTPRoute`.
|
||||
|
||||
Beispiel (Cloudflare-Gateway-API-Operator):
|
||||
|
||||
```yaml
|
||||
gateway:
|
||||
enabled: true
|
||||
name: bento-tunnel
|
||||
namespace: bentopdf
|
||||
gatewayClassName: cloudflare
|
||||
|
||||
httpRoute:
|
||||
enabled: true
|
||||
parentRefs:
|
||||
- name: bento-tunnel
|
||||
namespace: bentopdf
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- pdfs.example.com
|
||||
```
|
||||
|
||||
## Sicherstellen, dass die SharedArrayBuffer-Header weiterhin funktionieren (Ingress/Gateway)
|
||||
|
||||
### Was passieren "sollte"
|
||||
|
||||
Die Nginx-Konfiguration von BentoPDF setzt die erforderlichen Response-Header. Die meisten Ingress-/Gateway-Controller **leiten Response-Header des Upstreams unverändert weiter**.
|
||||
|
||||
### Was das verhindern kann
|
||||
|
||||
- Eine Controller-/Edge-Richtlinie, die Response-Header **überschreibt** oder **entfernt**
|
||||
- Eine "Security-Header"-Middleware, die andere COOP/COEP-Werte setzt
|
||||
|
||||
### So überprüfen Sie es
|
||||
|
||||
Führen Sie dies gegen Ihren öffentlichen Endpunkt aus:
|
||||
|
||||
```bash
|
||||
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
|
||||
```
|
||||
|
||||
Sie sollten Folgendes sehen:
|
||||
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### Wenn Ihr Ingress-Controller sie nicht beibehält
|
||||
|
||||
Fügen Sie die Header am Edge hinzu (controllerspezifisch). Beispiel für **nginx-ingress**:
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
```
|
||||
|
||||
## `.mjs`-MIME-Type-Fehler (PDF unterschreiben / PDF-Formular ausfüllen, leerer iframe)
|
||||
|
||||
Wenn die Browserkonsole bei einer `.mjs`-Anfrage `Failed to load module script: ... non-JavaScript MIME type "application/octet-stream"` anzeigt, ersetzt ein Ingress-/Gateway-Controller vor dem BentoPDF-Nginx den Upstream-Header `Content-Type` durch `application/octet-stream`.
|
||||
|
||||
Der Nginx im BentoPDF-Image liefert `.mjs` bereits als `application/javascript` aus. Die Lösung besteht darin, den Controller daran zu hindern, den Header zu entfernen oder zu ersetzen. Für nginx-ingress:
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
location ~* \.mjs$ {
|
||||
types {} default_type application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
Oder verwenden Sie in der Gateway API einen `ResponseHeaderModifier`-Filter, um `Content-Type: application/javascript` für `.mjs`-Pfade zu setzen. Überprüfen Sie es im DevTools-Tab Network: Die fehlgeschlagene `.mjs`-Anfrage sollte nun `content-type: application/javascript` zeigen.
|
||||
|
||||
### Wenn Sie die Gateway API nutzen und Header erzwingen möchten
|
||||
|
||||
Die Gateway API unterstützt einen `ResponseHeaderModifier`-Filter. Sie können ihn in `httpRoute.rules[*].filters` einbinden:
|
||||
|
||||
```yaml
|
||||
httpRoute:
|
||||
enabled: true
|
||||
hostnames: [pdf.example.com]
|
||||
parentRefs:
|
||||
- name: bento-tunnel
|
||||
namespace: misc
|
||||
sectionName: http
|
||||
rules:
|
||||
- matches:
|
||||
- path: { type: PathPrefix, value: / }
|
||||
filters:
|
||||
- type: ResponseHeaderModifier
|
||||
responseHeaderModifier:
|
||||
set:
|
||||
- name: Cross-Origin-Opener-Policy
|
||||
value: same-origin
|
||||
- name: Cross-Origin-Embedder-Policy
|
||||
value: require-corp
|
||||
```
|
||||
|
||||
Die Unterstützung bestimmter Filter hängt von Ihrem Gateway-Controller ab. Wenn ein Filter ignoriert wird, fügen Sie die Header stattdessen am Edge bzw. im Controller hinzu.
|
||||
|
||||
## Bestimmte Werkzeuge deaktivieren
|
||||
|
||||
Verwenden Sie eine ConfigMap, um Werkzeuge zur Laufzeit zu deaktivieren, ohne das Image neu zu bauen:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Binden Sie sie in das ausgelieferte Verzeichnis ein:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
- name: bentopdf
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /usr/share/nginx/html/config.json
|
||||
subPath: config.json
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: bentopdf-config
|
||||
```
|
||||
|
||||
Werkzeug-IDs entsprechen der Seiten-URL ohne `.html`: Öffnen Sie ein beliebiges Werkzeug und sehen Sie sich die URL an (z. B. `edit-pdf`, `merge-pdf`, `compress-pdf`). Deaktivierte Werkzeuge werden auf der Startseite, in der Suche, in den Tastenkürzeln, im Workflow-Builder und beim direkten URL-Zugriff ausgeblendet. Die vollständige Liste der Optionen finden Sie im [Docker-Leitfaden](/de/self-hosting/docker#bestimmte-werkzeuge-deaktivieren).
|
||||
@@ -0,0 +1,139 @@
|
||||
# Auf Netlify bereitstellen
|
||||
|
||||
[Netlify](https://netlify.com) bietet hervorragendes Hosting für statische Websites mit einem großzügigen kostenlosen Kontingent.
|
||||
|
||||
## Bereitstellung mit einem Klick
|
||||
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Manuelle Bereitstellung
|
||||
|
||||
### Schritt 1: Repository verbinden
|
||||
|
||||
1. Melden Sie sich bei [Netlify](https://app.netlify.com) an
|
||||
2. Klicken Sie auf "Add new site" → "Import an existing project"
|
||||
3. Verbinden Sie Ihr GitHub-Konto
|
||||
4. Wählen Sie Ihren BentoPDF-Fork aus
|
||||
|
||||
### Schritt 2: Build-Einstellungen konfigurieren
|
||||
|
||||
| Einstellung | Wert |
|
||||
| ----------------- | --------------- |
|
||||
| Build command | `npm run build` |
|
||||
| Publish directory | `dist` |
|
||||
| Node version | 18+ |
|
||||
|
||||
### Schritt 3: Bereitstellen
|
||||
|
||||
Klicken Sie auf "Deploy site" und warten Sie auf den Build.
|
||||
|
||||
## Konfigurationsdatei
|
||||
|
||||
Erstellen Sie `netlify.toml` im Stammverzeichnis Ihres Projekts:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
command = "npm run build"
|
||||
publish = "dist"
|
||||
|
||||
[build.environment]
|
||||
NODE_VERSION = "18"
|
||||
|
||||
# Required security headers for SharedArrayBuffer (used by LibreOffice WASM)
|
||||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
Cross-Origin-Embedder-Policy = "require-corp"
|
||||
Cross-Origin-Opener-Policy = "same-origin"
|
||||
Cross-Origin-Resource-Policy = "cross-origin"
|
||||
|
||||
# Pre-compressed LibreOffice WASM binary - must be served with Content-Encoding
|
||||
[[headers]]
|
||||
for = "/libreoffice-wasm/soffice.wasm.gz"
|
||||
[headers.values]
|
||||
Content-Type = "application/wasm"
|
||||
Content-Encoding = "gzip"
|
||||
Cache-Control = "public, max-age=31536000, immutable"
|
||||
|
||||
# Pre-compressed LibreOffice WASM data - must be served with Content-Encoding
|
||||
[[headers]]
|
||||
for = "/libreoffice-wasm/soffice.data.gz"
|
||||
[headers.values]
|
||||
Content-Type = "application/octet-stream"
|
||||
Content-Encoding = "gzip"
|
||||
Cache-Control = "public, max-age=31536000, immutable"
|
||||
|
||||
# Cache other WASM files
|
||||
[[headers]]
|
||||
for = "*.wasm"
|
||||
[headers.values]
|
||||
Cache-Control = "public, max-age=31536000, immutable"
|
||||
Content-Type = "application/wasm"
|
||||
|
||||
# SPA routing
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
```
|
||||
|
||||
::: warning Wichtig
|
||||
Die Header `Cross-Origin-Embedder-Policy` und `Cross-Origin-Opener-Policy` sind für die Konvertierung von Word/ODT/Excel/PowerPoint in PDF erforderlich. Ohne sie ist `SharedArrayBuffer` nicht verfügbar, und die LibreOffice-WASM-Engine kann nicht initialisiert werden.
|
||||
|
||||
Die `Content-Encoding: gzip`-Header für die Dateien `.wasm.gz` und `.data.gz` weisen den Browser an, sie automatisch zu dekomprimieren. Ohne sie erhält der Browser rohe gzip-Bytes, und die WASM-Kompilierung schlägt fehl.
|
||||
:::
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
Legen Sie diese unter Site settings → Environment variables fest:
|
||||
|
||||
| Variable | Beschreibung |
|
||||
| ----------------------- | ---------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Auf `true` setzen für einen minimalen Build |
|
||||
| `VITE_BRAND_NAME` | Eigener Markenname (ersetzt "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logopfad relativ zu `public/` (z. B. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Eigener Footer-/Copyright-Text |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standardsprache der Oberfläche (z. B. `fr`, `de`, `es`) |
|
||||
|
||||
## Eigene Domain
|
||||
|
||||
1. Gehen Sie zu Site settings → Domain management
|
||||
2. Klicken Sie auf "Add custom domain"
|
||||
3. Folgen Sie den Anweisungen zur DNS-Konfiguration
|
||||
|
||||
## Große Mediendateien
|
||||
|
||||
Für große WASM-Dateien können Sie [Netlify Large Media](https://docs.netlify.com/large-media/overview/) aktivieren:
|
||||
|
||||
```bash
|
||||
netlify lm:setup
|
||||
git lfs track "*.wasm"
|
||||
```
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Word/ODT/Excel zu PDF bleibt bei 55 % hängen
|
||||
|
||||
Wenn Dokumentkonvertierungen bei 55 % hängen bleiben, öffnen Sie die DevTools-Konsole und prüfen Sie:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Wenn `crossOriginIsolated` den Wert `false` hat, werden die COEP/COOP-Header aus Ihrer `netlify.toml` nicht angewendet. Stellen Sie sicher, dass sich die Datei im Stammverzeichnis Ihres Projekts befindet, und stellen Sie erneut bereit.
|
||||
|
||||
Wenn Sie in der Konsole `expected magic word 00 61 73 6d, found 1f 8b 08 08` sehen, fehlen den `.wasm.gz`-Dateien die `Content-Encoding: gzip`-Header. Stellen Sie sicher, dass die `[[headers]]`-Blöcke für `soffice.wasm.gz` und `soffice.data.gz` in Ihrer `netlify.toml` enthalten sind.
|
||||
|
||||
### Build schlägt fehl
|
||||
|
||||
Prüfen Sie die Kompatibilität der Node-Version:
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
```
|
||||
|
||||
### Langsames erstes Laden
|
||||
|
||||
Aktivieren Sie die Asset-Optimierung unter Site settings → Build & deploy → Asset optimization.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Mit Nginx bereitstellen
|
||||
|
||||
Hosten Sie BentoPDF mit Nginx auf Ihrem eigenen Server.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Ubuntu-/Debian-Server
|
||||
- Installierter Nginx
|
||||
- SSL-Zertifikat (empfohlen: Let's Encrypt)
|
||||
|
||||
## Schritt 1: Das Projekt bauen
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Um das Branding anzupassen, setzen Sie vor dem Build Umgebungsvariablen:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Schritt 2: Dateien kopieren
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Schritt 3: Nginx-Konfiguration
|
||||
|
||||
Erstellen Sie `/etc/nginx/sites-available/bentopdf`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
||||
|
||||
root /var/www/bentopdf;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/wasm;
|
||||
gzip_min_length 1000;
|
||||
|
||||
# WASM MIME type
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
|
||||
# Required headers for SharedArrayBuffer (LibreOffice WASM)
|
||||
# These must be on every response - especially HTML pages
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Pre-compressed LibreOffice WASM binary
|
||||
location ~* /libreoffice-wasm/soffice\.wasm\.gz$ {
|
||||
gzip off;
|
||||
types {} default_type application/wasm;
|
||||
add_header Content-Encoding gzip;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# Pre-compressed LibreOffice WASM data
|
||||
location ~* /libreoffice-wasm/soffice\.data\.gz$ {
|
||||
gzip off;
|
||||
types {} default_type application/octet-stream;
|
||||
add_header Content-Encoding gzip;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|wasm)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# SPA routing - serve index.html for all routes
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Schritt 4: Die Site aktivieren
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Schritt 5: SSL mit Let's Encrypt
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
## Bereitstellung in einem Unterverzeichnis
|
||||
|
||||
Um unter `/pdf/` zu hosten:
|
||||
|
||||
```nginx
|
||||
location /pdf/ {
|
||||
alias /var/www/bentopdf/;
|
||||
try_files $uri $uri/ /pdf/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
Bauen Sie mit:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
## Performance-Tuning
|
||||
|
||||
Fügen Sie in `nginx.conf` hinzu:
|
||||
|
||||
```nginx
|
||||
http {
|
||||
# Enable sendfile
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
|
||||
# Increase buffer sizes
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Worker connections
|
||||
worker_connections 2048;
|
||||
}
|
||||
```
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### WASM wird nicht geladen
|
||||
|
||||
Stellen Sie sicher, dass der MIME-Type gesetzt ist:
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
### Signieren oder Formularausfüllung zeigt einen leeren Viewer (`.mjs`-MIME-Fehler)
|
||||
|
||||
Wenn die Browserkonsole Folgendes anzeigt:
|
||||
|
||||
```
|
||||
Failed to load module script: The server responded with a non-JavaScript
|
||||
MIME type of "application/octet-stream". Strict MIME type checking is
|
||||
enforced for module scripts per HTML spec.
|
||||
```
|
||||
|
||||
…und die fehlgeschlagene Anfrage auf `.mjs` endet, fehlt in Ihrem Nginx die Zuordnung `.mjs → application/javascript`. Der mitgelieferte PDF-Viewer (genutzt von PDF unterschreiben, PDF-Formular ausfüllen und einigen weiteren Werkzeugen) enthält ES-Moduldateien mit der Endung `.mjs`, und die Standard-`mime.types` von Nginx enthält diese nicht.
|
||||
|
||||
Beheben Sie es, indem Sie `mjs` in der bestehenden `application/javascript`-Zeile in `/etc/nginx/mime.types` ergänzen:
|
||||
|
||||
```nginx
|
||||
application/javascript js mjs;
|
||||
```
|
||||
|
||||
> Fügen Sie KEINE _zweite_ `application/javascript`-Zeile hinzu: Nginx liest `mime.types` Zeile für Zeile und nur eine Zuordnung von Typ zu Endung gilt, sodass eine zweite Zeile in manchen Versionen stillschweigend ignoriert wird. Bearbeiten Sie die bestehende Zeile und ergänzen Sie `mjs` neben `js`.
|
||||
|
||||
Oder überschreiben Sie es lokal in Ihrem Server-Block (ohne Änderung der globalen Datei):
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
Laden Sie Nginx nach der Änderung neu:
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Zur Bestätigung: Suchen Sie in den Browser-DevTools im Tab Network die fehlgeschlagene `.mjs`-Anfrage und prüfen Sie den Response-Header `Content-Type`. Er sollte jetzt `application/javascript` lauten, nicht `application/octet-stream`.
|
||||
|
||||
### Word/ODT/Excel zu PDF funktioniert nicht
|
||||
|
||||
LibreOffice-WASM benötigt `SharedArrayBuffer`, wofür die Header `Cross-Origin-Embedder-Policy` und `Cross-Origin-Opener-Policy` nötig sind. Außerdem wird ein sicherer Kontext benötigt: `http://localhost` funktioniert für lokale Tests, aber `http://192.168.x.x` oder andere LAN-IPs erfordern in der Regel HTTPS. Beachten Sie, dass `add_header`-Direktiven in einem `location`-Block die `add_header`-Direktiven auf Server-Ebene **überschreiben**, statt sich mit ihnen zu vereinen. Jeder `location`-Block mit eigenem `add_header` muss die COEP/COOP-Header enthalten.
|
||||
|
||||
Überprüfen Sie mit:
|
||||
|
||||
```bash
|
||||
curl -I https://your-domain.com/ | grep -i cross-origin
|
||||
```
|
||||
|
||||
Wenn ein Reverse-Proxy vor Nginx liegt, stellen Sie sicher, dass er diese Header beibehält.
|
||||
|
||||
### 502 Bad Gateway
|
||||
|
||||
Prüfen Sie die Nginx-Fehlerprotokolle:
|
||||
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
# Auf Vercel bereitstellen
|
||||
|
||||
[Vercel](https://vercel.com) bietet die einfachste Bereitstellung für statische Websites.
|
||||
|
||||
## Bereitstellung mit einem Klick
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Manuelle Bereitstellung
|
||||
|
||||
### Schritt 1: Repository forken
|
||||
|
||||
Forken Sie [bentopdf/bentopdf](https://github.com/alam00000/bentopdf) in Ihr GitHub-Konto.
|
||||
|
||||
### Schritt 2: In Vercel importieren
|
||||
|
||||
1. Gehen Sie zu [vercel.com/new](https://vercel.com/new)
|
||||
2. Wählen Sie Ihr geforktes Repository aus
|
||||
3. Konfigurieren Sie das Projekt:
|
||||
|
||||
| Einstellung | Wert |
|
||||
| ---------------- | --------------- |
|
||||
| Framework Preset | Vite |
|
||||
| Build Command | `npm run build` |
|
||||
| Output Directory | `dist` |
|
||||
| Install Command | `npm install` |
|
||||
|
||||
### Schritt 3: Umgebungsvariablen (optional)
|
||||
|
||||
Fügen Sie diese bei Bedarf hinzu:
|
||||
|
||||
| Variable | Beschreibung |
|
||||
| ----------------------- | ---------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Auf `true` setzen für eine minimale Oberfläche |
|
||||
| `VITE_BRAND_NAME` | Eigener Markenname (ersetzt "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logopfad relativ zu `public/` (z. B. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Eigener Footer-/Copyright-Text |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standardsprache der Oberfläche (z. B. `fr`, `de`, `es`) |
|
||||
|
||||
### Schritt 4: Bereitstellen
|
||||
|
||||
Klicken Sie auf "Deploy" und warten Sie, bis der Build abgeschlossen ist.
|
||||
|
||||
## Eigene Domain
|
||||
|
||||
1. Öffnen Sie die Einstellungen Ihres Projekts
|
||||
2. Navigieren Sie zu "Domains"
|
||||
3. Fügen Sie Ihre eigene Domain hinzu
|
||||
4. Konfigurieren Sie das DNS wie angegeben
|
||||
|
||||
## Einschränkungen
|
||||
|
||||
::: warning Große Dateien
|
||||
Die Serverless Functions von Vercel haben ein Limit von 50 MB. Da BentoPDF eine statische Website ist, sollte Sie das nicht betreffen, aber die WASM-Module sind groß (insgesamt ca. 100 MB). Stellen Sie sicher, dass sie aus dem Ordner `/public` ausgeliefert werden.
|
||||
:::
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Build-Timeout
|
||||
|
||||
Wenn Builds in ein Timeout laufen, versuchen Sie Folgendes:
|
||||
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 beim Neuladen
|
||||
|
||||
Fügen Sie eine `vercel.json` für das SPA-Routing hinzu:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
```
|
||||
|
||||
### Word/ODT/Excel zu PDF funktioniert nicht
|
||||
|
||||
Die LibreOffice-WASM-Konvertierungen erfordern `SharedArrayBuffer`, wofür diese Response-Header auf allen Seiten nötig sind:
|
||||
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
|
||||
Die vorkomprimierten Dateien `.wasm.gz` und `.data.gz` benötigen außerdem `Content-Encoding: gzip`, damit der Browser sie dekomprimiert.
|
||||
|
||||
Fügen Sie diese zu Ihrer `vercel.json` hinzu:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
|
||||
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "key": "Cross-Origin-Resource-Policy", "value": "cross-origin" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/libreoffice-wasm/soffice.wasm.gz",
|
||||
"headers": [
|
||||
{ "key": "Content-Type", "value": "application/wasm" },
|
||||
{ "key": "Content-Encoding", "value": "gzip" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/libreoffice-wasm/soffice.data.gz",
|
||||
"headers": [
|
||||
{ "key": "Content-Type", "value": "application/octet-stream" },
|
||||
{ "key": "Content-Encoding", "value": "gzip" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Zur Überprüfung öffnen Sie die DevTools-Konsole und führen Sie `console.log(window.crossOriginIsolated)` aus. Es sollte `true` zurückgeben.
|
||||
Reference in new issue
Block a user