mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-13 17:54:19 +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 @@
|
||||
# Implementeren met Apache
|
||||
|
||||
Host BentoPDF met Apache HTTP Server.
|
||||
|
||||
## Vereisten
|
||||
|
||||
- Apache 2.4+
|
||||
- mod_rewrite ingeschakeld
|
||||
- SSL-certificaat (aanbevolen)
|
||||
|
||||
## Stap 1: Bouw het project
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Wil je de huisstijl aanpassen, stel dan omgevingsvariabelen in voordat je bouwt:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Stap 2: Kopieer de bestanden
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Stap 3: Apache-configuratie
|
||||
|
||||
Maak `/etc/apache2/sites-available/bentopdf.conf` aan:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
## Stap 4: .htaccess voor routing
|
||||
|
||||
Maak `/var/www/bentopdf/.htaccess` aan:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Stap 5: Schakel de vereiste modules in
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
sudo a2enmod ssl
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod expires
|
||||
sudo a2enmod deflate
|
||||
```
|
||||
|
||||
## Stap 6: Schakel de site in
|
||||
|
||||
```bash
|
||||
sudo a2ensite bentopdf.conf
|
||||
sudo apache2ctl configtest
|
||||
sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
## Implementatie in een submap
|
||||
|
||||
Om te hosten op `/pdf/`:
|
||||
|
||||
1. Bouw met de basis-URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
2. Werk `.htaccess` bij:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### WASM 404-fouten
|
||||
|
||||
Controleer of het MIME-type is geconfigureerd:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### PDF ondertekenen of formulierinvuller toont een leeg weergavevenster (`.mjs` MIME-fout)
|
||||
|
||||
Als de browserconsole dit toont:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
...en het mislukte verzoek eindigt op `.mjs`, ontbreekt in je Apache-configuratie de koppeling `.mjs → application/javascript`. De meegeleverde PDF-viewer bevat ES-modulebestanden met de extensie `.mjs`, en de standaard `mime.types` van Apache bevat die niet.
|
||||
|
||||
Voeg toe aan je VirtualHost of `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/javascript .mjs
|
||||
```
|
||||
|
||||
Laad Apache daarna opnieuw:
|
||||
|
||||
```bash
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
Controleren: zoek in de browser-DevTools op het tabblad Network het mislukte `.mjs`-verzoek en bekijk de responsheader `Content-Type`. Die moet nu `application/javascript` zijn, niet `application/octet-stream`.
|
||||
|
||||
### Rewrite werkt niet
|
||||
|
||||
Controleer of mod_rewrite is ingeschakeld:
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
### Word/ODT/Excel naar PDF werkt niet
|
||||
|
||||
LibreOffice WASM vereist `SharedArrayBuffer`, waarvoor deze headers nodig zijn:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
Daarnaast is een beveiligde context nodig. `http://localhost` werkt voor lokaal testen, maar `http://192.168.x.x` of andere LAN-IP's vereisen meestal HTTPS. Als de headers aanwezig zijn maar `window.crossOriginIsolated` nog steeds `false` is, controleer dan of de pagina via gewoon HTTP wordt geopend op een origin die geen loopback is.
|
||||
|
||||
De vooraf gecomprimeerde bestanden `.wasm.gz` en `.data.gz` hebben ook de juiste `Content-Encoding` nodig:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
Zorg dat `mod_headers` is ingeschakeld: `sudo a2enmod headers`
|
||||
|
||||
### Toegang geweigerd
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
sudo chmod -R 755 /var/www/bentopdf
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
# Implementeren op AWS S3 + CloudFront
|
||||
|
||||
Host BentoPDF op AWS voor maximale controle en schaalbaarheid.
|
||||
|
||||
## Architectuur
|
||||
|
||||
```
|
||||
User → CloudFront (CDN) → S3 (Static Files)
|
||||
```
|
||||
|
||||
## Stap 1: Maak een S3-bucket
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Stap 2: Bouwen en uploaden
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Stap 3: Maak een CloudFront-distributie
|
||||
|
||||
```bash
|
||||
aws cloudfront create-distribution \
|
||||
--origin-domain-name your-bentopdf-bucket.s3.amazonaws.com \
|
||||
--default-root-object index.html
|
||||
```
|
||||
|
||||
Of gebruik de AWS Console:
|
||||
|
||||
1. Ga naar CloudFront → Create distribution
|
||||
2. Origin domain: selecteer je S3-bucket
|
||||
3. Schakel "Origin Access Control" in
|
||||
4. Default root object: `index.html`
|
||||
5. Maak de distributie aan
|
||||
|
||||
## Stap 3b: Response Headers Policy (vereist voor LibreOffice WASM)
|
||||
|
||||
Conversies met LibreOffice (Word, Excel, PowerPoint naar PDF) vereisen `SharedArrayBuffer`, waarvoor specifieke responsheaders nodig zijn. Maak een CloudFront Response Headers Policy aan:
|
||||
|
||||
1. Ga naar CloudFront → Policies → Response headers
|
||||
2. Maak een aangepaste policy met deze headers:
|
||||
|
||||
| Header | Waarde |
|
||||
| ------------------------------ | -------------- |
|
||||
| `Cross-Origin-Embedder-Policy` | `require-corp` |
|
||||
| `Cross-Origin-Opener-Policy` | `same-origin` |
|
||||
| `Cross-Origin-Resource-Policy` | `cross-origin` |
|
||||
|
||||
3. Koppel de policy aan het standaard cachegedrag van je distributie
|
||||
|
||||
Of via de 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}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Stap 3c: S3-metadata voor vooraf gecomprimeerde WASM-bestanden
|
||||
|
||||
De LibreOffice WASM-bestanden zijn vooraf gecomprimeerd (`.wasm.gz`, `.data.gz`). Stel de juiste Content-Type en Content-Encoding in, zodat browsers ze decomprimeren:
|
||||
|
||||
```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 Belangrijk
|
||||
Zonder de response headers policy is `SharedArrayBuffer` niet beschikbaar en blijven LibreOffice WASM-conversies hangen op ~55%. Zonder de juiste Content-Encoding op de `.gz`-bestanden ontvangt de browser onbewerkte gzip-bytes en mislukt de WASM-compilatie.
|
||||
:::
|
||||
|
||||
## Stap 4: S3-bucketbeleid
|
||||
|
||||
Geef CloudFront toegang tot de 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Stap 5: Aangepaste foutpagina's
|
||||
|
||||
Configureer 404 zo dat `index.html` wordt geretourneerd voor SPA-routing:
|
||||
|
||||
1. CloudFront → Error pages
|
||||
2. Maak een aangepaste foutrespons:
|
||||
- HTTP error code: 404
|
||||
- Response page path: `/index.html`
|
||||
- HTTP response code: 200
|
||||
|
||||
## Kostenraming
|
||||
|
||||
| Resource | Geschatte kosten |
|
||||
| -------------------------- | ---------------- |
|
||||
| S3-opslag (~500MB) | ~$0,01/maand |
|
||||
| CloudFront (1TB transfer) | ~$85/maand |
|
||||
| CloudFront (10GB transfer) | ~$0,85/maand |
|
||||
|
||||
::: tip
|
||||
Gebruik S3 Intelligent Tiering voor kostenoptimalisatie bij bestanden die zelden worden geopend.
|
||||
:::
|
||||
|
||||
## Automatisering met 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 @@
|
||||
# Implementeren op Cloudflare Pages
|
||||
|
||||
[Cloudflare Pages](https://pages.cloudflare.com) biedt snelle, wereldwijde hosting van statische sites met onbeperkte bandbreedte.
|
||||
|
||||
## Snel implementeren
|
||||
|
||||
1. Ga naar [Cloudflare Pages](https://dash.cloudflare.com/?to=/:account/pages)
|
||||
2. Klik op "Create a project"
|
||||
3. Koppel je GitHub-repository
|
||||
|
||||
## Buildconfiguratie
|
||||
|
||||
| Instelling | Waarde |
|
||||
| ---------------------- | --------------- |
|
||||
| Framework preset | None |
|
||||
| Build command | `npm run build` |
|
||||
| Build output directory | `dist` |
|
||||
| Root directory | `/` |
|
||||
|
||||
## Omgevingsvariabelen
|
||||
|
||||
Voeg deze toe onder Settings → Environment variables:
|
||||
|
||||
| Variabele | Waarde |
|
||||
| ----------------------- | ------------------------------------------------------ |
|
||||
| `NODE_VERSION` | `18` |
|
||||
| `SIMPLE_MODE` | `false` (optioneel) |
|
||||
| `VITE_BRAND_NAME` | Aangepaste merknaam (optioneel) |
|
||||
| `VITE_BRAND_LOGO` | Logopad relatief aan `public/` (optioneel) |
|
||||
| `VITE_FOOTER_TEXT` | Aangepaste footer-/copyrighttekst (optioneel) |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standaardtaal van de interface, bijv. `fr` (optioneel) |
|
||||
|
||||
## Configuratiebestand
|
||||
|
||||
Maak `_headers` aan in je map `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 Belangrijk
|
||||
De headers `Cross-Origin-Embedder-Policy` en `Cross-Origin-Opener-Policy` zijn vereist voor conversies van Word/ODT/Excel/PowerPoint naar PDF. Zonder deze headers is `SharedArrayBuffer` niet beschikbaar en kan de LibreOffice WASM-engine niet starten.
|
||||
:::
|
||||
|
||||
Maak `_redirects` aan voor SPA-routing:
|
||||
|
||||
```
|
||||
/* /index.html 200
|
||||
```
|
||||
|
||||
## Eigen domein
|
||||
|
||||
1. Ga naar je Pages-project
|
||||
2. Klik op "Custom domains"
|
||||
3. Voeg je domein toe
|
||||
4. Cloudflare configureert DNS automatisch als het domein bij Cloudflare staat
|
||||
|
||||
## Voordelen
|
||||
|
||||
- **Gratis onbeperkte bandbreedte**
|
||||
- **Wereldwijd CDN** met 300+ edge-locaties
|
||||
- **Automatische HTTPS**
|
||||
- **Previewimplementaties** voor pull requests
|
||||
- **Snelle builds**
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### Uploads van grote bestanden
|
||||
|
||||
Cloudflare Pages ondersteunt bestanden tot 25 MB. WASM-modules zouden geen probleem moeten zijn, maar als je tegen limieten aanloopt, overweeg dan:
|
||||
|
||||
```bash
|
||||
# Split large files during build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Groottelimieten van Workers
|
||||
|
||||
Gebruik je Cloudflare Workers voor geavanceerde routing, let dan op de limiet van 1 MB voor gratis abonnementen.
|
||||
|
||||
## CORS-proxyworker (voor digitale handtekeningen)
|
||||
|
||||
De tool voor digitale handtekeningen vereist een CORS-proxy om certificaatketens op te halen. Implementeer de meegeleverde worker:
|
||||
|
||||
```bash
|
||||
cd cloudflare
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Beveiligingsfuncties
|
||||
|
||||
| Functie | Beschrijving |
|
||||
| ------------------------- | ----------------------------------- |
|
||||
| **URL-beperkingen** | Alleen certificaat-URL's toegestaan |
|
||||
| **Bestandsgroottelimiet** | Max 10MB per verzoek |
|
||||
| **Rate limiting** | 60 verzoeken/IP/min (vereist KV) |
|
||||
| **Blokkering privé-IP's** | Blokkeert localhost en interne IP's |
|
||||
|
||||
### Rate limiting inschakelen
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Bouwen met proxy-URL
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
Of met 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 .
|
||||
```
|
||||
|
||||
> **Opmerking:** Zie de [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required) voor de installatie van de HMAC-handtekening.
|
||||
@@ -0,0 +1,180 @@
|
||||
# CORS-proxy voor het ophalen van certificaten
|
||||
|
||||
De tool voor digitale handtekeningen gebruikt een CORS-proxy om uitgevers-certificaten op te halen bij externe certificeringsinstanties (CA's). Dat is nodig omdat veel CA-servers geen CORS-headers in hun antwoorden opnemen, waardoor rechtstreeks ophalen vanuit de browser niet lukt.
|
||||
|
||||
Bovendien leveren veel CA-servers certificaten via gewoon HTTP. Wanneer je BentoPDF-instantie via HTTPS wordt gehost, blokkeren browsers deze HTTP-verzoeken (mixed content-beleid). De CORS-proxy lost beide problemen op door verzoeken via een HTTPS-eindpunt met de juiste headers te routeren.
|
||||
|
||||
## Hoe het werkt
|
||||
|
||||
Bij het ondertekenen van een PDF met een certificaat:
|
||||
|
||||
1. De bibliotheek `zgapdfsigner` probeert een volledige certificaatketen op te bouwen
|
||||
2. Ze haalt uitgevers-certificaten op via URL's in de AIA-extensie (Authority Information Access) van je certificaat
|
||||
3. Deze verzoeken lopen via een CORS-proxy die de benodigde `Access-Control-Allow-Origin`-headers toevoegt
|
||||
4. De proxy stuurt de certificaatgegevens terug naar de browser
|
||||
|
||||
## De CORS-proxy zelf hosten
|
||||
|
||||
Host je BentoPDF zelf, dan moet je een eigen CORS-proxy implementeren, zodat digitale handtekeningen werken met certificaten waarvoor de keten moet worden opgehaald.
|
||||
|
||||
### Optie 1: Cloudflare Workers (aanbevolen)
|
||||
|
||||
1. **Installeer de Wrangler CLI**:
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
```
|
||||
|
||||
2. **Log in bij Cloudflare**:
|
||||
|
||||
```bash
|
||||
wrangler login
|
||||
```
|
||||
|
||||
3. **Clone BentoPDF en pas de toegestane origins aan**:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf/cloudflare
|
||||
```
|
||||
|
||||
Open `cors-proxy-worker.js` en verander de array `ALLOWED_ORIGINS` naar je eigen domein:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = [
|
||||
'https://your-domain.com',
|
||||
'https://www.your-domain.com',
|
||||
];
|
||||
```
|
||||
|
||||
::: warning Belangrijk
|
||||
Zonder deze wijziging weigert de proxy alle verzoeken van je site met een **403 Forbidden**-fout. Standaard worden alleen verzoeken van `bentopdf.com` toegestaan.
|
||||
:::
|
||||
|
||||
4. **Implementeer de proxy**:
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Noteer de URL van je worker (bijv. `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
|
||||
|
||||
5. **Bouw BentoPDF opnieuw met de proxy-URL**:
|
||||
|
||||
Met 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 .
|
||||
```
|
||||
|
||||
Bouwen vanuit de broncode:
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
### Optie 2: Eigen backendproxy
|
||||
|
||||
Je kunt ook je eigen proxy-eindpunt maken. De vereisten zijn:
|
||||
|
||||
1. Accepteer GET-verzoeken met een queryparameter `url`
|
||||
2. Haal de URL op vanaf je server (aan de serverkant gelden geen CORS-beperkingen)
|
||||
3. Retourneer het antwoord met deze headers:
|
||||
- `Access-Control-Allow-Origin: https://your-domain.com`
|
||||
- `Access-Control-Allow-Methods: GET, OPTIONS`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
|
||||
Voorbeeldimplementatie met 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' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Beveiligingsoverwegingen
|
||||
|
||||
De meegeleverde Cloudflare Worker heeft diverse beveiligingsmaatregelen:
|
||||
|
||||
| Functie | Beschrijving |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| **Origin-validatie** | Staat alleen verzoeken toe van domeinen in `ALLOWED_ORIGINS` |
|
||||
| **URL-beperkingen** | Staat alleen certificaat-URL's toe (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`) |
|
||||
| **Blokkering privé-IP's** | Blokkeert privébereiken van IPv4/IPv6, link-local, loopback, decimale IP's en cloud-metadata |
|
||||
| **Content-Type-veiligheid** | Retourneert alleen veilige MIME-typen voor certificaten en blokkeert injectie van upstream content-type |
|
||||
| **Bestandsgroottelimiet** | Streamt het antwoord met een limiet van 10MB en breekt halverwege de download af bij overschrijding |
|
||||
| **Rate limiting** | 60 verzoeken per minuut per IP of IPv6 /64 (vereist KV, laat verkeer door bij KV-fouten) |
|
||||
| **HMAC-handtekeningen** | Optionele ondertekening aan de clientzijde (ontmoedigt gelegenheidsmisbruik) |
|
||||
|
||||
### DNS-rebinding (zelf hosten buiten Cloudflare)
|
||||
|
||||
De worker weigert elke URL die naar een privé- of intern IP verwijst. Een vaste allowlist van hosts is niet mogelijk, omdat URL's in certificaatketens verwijzen naar de CA die het certificaat heeft uitgegeven (FNMT, bedrijfs-CA's enzovoort), dus de bestemmingen zijn vooraf niet bekend. Daardoor blijft een klein rebinding-venster over: wie de DNS van een eigen domein beheert, kan de veiligheidscontrole een publiek IP geven en het echte verzoek even later een privé-IP.
|
||||
|
||||
Op Cloudflare is dit niet te misbruiken: Workers kunnen geen privé-IP's of het metadata-eindpunt `169.254.169.254` bereiken, dus een rebind komt nergens uit. Geef de worker alleen geen toegang naar binnen: koppel geen Tunnel, Service of privénetwerkbinding (hij heeft alleen `RATE_LIMIT_KV` nodig).
|
||||
|
||||
Draai je dit buiten Cloudflare (workerd, Node en dergelijke) in een VPC, dan _kan_ de runtime interne IP's bereiken. Zet er een egress-firewall voor en blokkeer de privébereiken: `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` en hun IPv6-equivalenten. Applicatiecode kan dat niet afdwingen zodra je buiten Cloudflare draait.
|
||||
|
||||
## De proxy uitschakelen
|
||||
|
||||
Wil je geen CORS-proxy gebruiken, stel de omgevingsvariabele dan in op een lege string:
|
||||
|
||||
```
|
||||
VITE_CORS_PROXY_URL=
|
||||
```
|
||||
|
||||
**Opmerking**: Zonder de proxy mislukt het ondertekenen met certificaten waarvoor de keten extern moet worden opgehaald (zoals FNMT of sommige bedrijfs-CA's) met de foutmelding "Failed to fetch".
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### "Signing error: TypeError: Failed to fetch"
|
||||
|
||||
Dit betekent meestal een van deze dingen:
|
||||
|
||||
1. **Geen CORS-proxy geconfigureerd**: stel `VITE_CORS_PROXY_URL` in en bouw opnieuw
|
||||
2. **Mixed content geblokkeerd**: je site is HTTPS, maar de uitgevers-URL van het certificaat is HTTP. De CORS-proxy lost dit op.
|
||||
3. **CORS-proxy weigert je origin**: controleer of je domein in de array `ALLOWED_ORIGINS` in `cors-proxy-worker.js` staat
|
||||
|
||||
### "403 Forbidden" van de proxy
|
||||
|
||||
Je domein staat niet in de lijst `ALLOWED_ORIGINS`. Bewerk `cors-proxy-worker.js`:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = ['https://your-domain.com'];
|
||||
```
|
||||
|
||||
Implementeer daarna opnieuw: `npx wrangler deploy`
|
||||
|
||||
### De proxy testen
|
||||
|
||||
```bash
|
||||
curl -H "Origin: https://your-domain.com" \
|
||||
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"
|
||||
```
|
||||
|
||||
### Certificaten die zonder proxy werken
|
||||
|
||||
Sommige certificaten bevatten de volledige keten in het P12/PFX-bestand en hoeven niets extern op te halen:
|
||||
|
||||
- Zelfondertekende certificaten
|
||||
- Sommige commerciële CA's die tussencertificaten meeleveren
|
||||
- Certificaten die je handmatig met de volledige keten hebt samengesteld
|
||||
@@ -0,0 +1,622 @@
|
||||
# Implementeren met Docker / Podman
|
||||
|
||||
De eenvoudigste manier om BentoPDF zelf te hosten in een productieomgeving.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Vereiste headers voor conversie van Office-bestanden**
|
||||
>
|
||||
> Op LibreOffice gebaseerde tools (conversie van Word, Excel en PowerPoint) hebben deze HTTP-headers nodig voor `SharedArrayBuffer`-ondersteuning:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> De pagina moet ook vanuit een beveiligde context worden aangeboden. `http://localhost` werkt voor lokaal testen, maar `http://192.168.x.x` of andere LAN-IP's komen meestal niet in aanmerking, dus Office-conversie via gewoon HTTP mislukt ook als de headers aanwezig zijn.
|
||||
>
|
||||
> De officiële containerimages bevatten deze headers. Gebruik je een reverse proxy (Traefik, Caddy enzovoort), zorg dan dat deze headers behouden blijven of worden toegevoegd, en gebruik HTTPS voor toegang buiten loopback.
|
||||
|
||||
> [!TIP]
|
||||
> **Podman-gebruikers:** Alle `docker`-opdrachten werken met Podman door `docker` te vervangen door `podman` en `docker-compose` door `podman-compose`.
|
||||
|
||||
## Snelstart
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF wordt in twee builds geleverd:
|
||||
>
|
||||
> - **Zelfgehoste build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Alle PDF-tools, **zonder** de marketingelementen (geen hero, FAQ, testimonials, footer). Gebruik deze voor interne implementaties van teams of organisaties. Het is **geen** "lite"-versie met minder functies.
|
||||
> - **Commerciële build**: `ghcr.io/alam00000/bentopdf:latest`. De volledige marketingsite, gebruikt door bentopdf.com zelf en door houders van een commerciële licentie met openbare implementaties.
|
||||
>
|
||||
> Twijfel je, haal dan de zelfgehoste build op.
|
||||
|
||||
```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
|
||||
|
||||
Maak `docker-compose.yml` aan:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Uitvoeren:
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Bouw je eigen image
|
||||
|
||||
```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;"]
|
||||
```
|
||||
|
||||
Bouwen en uitvoeren:
|
||||
|
||||
```bash
|
||||
docker build -t bentopdf:custom .
|
||||
docker run -d -p 3000:8080 bentopdf:custom
|
||||
```
|
||||
|
||||
## Omgevingsvariabelen
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Bouw de zelfgehoste variant (verbergt de marketingelementen van bentopdf.com: hero, FAQ, testimonials, footer). Alle PDF-tools blijven beschikbaar. | `false` |
|
||||
| `BASE_URL` | Implementeren in een submap | `/` |
|
||||
| `VITE_WASM_PYMUPDF_URL` | URL van de PyMuPDF WASM-module | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/` |
|
||||
| `VITE_WASM_GS_URL` | URL van de Ghostscript WASM-module | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/` |
|
||||
| `VITE_WASM_CPDF_URL` | URL van de CoherentPDF WASM-module | `https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/` |
|
||||
| `VITE_TESSERACT_WORKER_URL` | URL van het OCR-workerscript | _(leeg; gebruik de standaard-CDN van Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_CORE_URL` | Map met de OCR-core-runtime | _(leeg; gebruik de standaard-CDN van Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_LANG_URL` | Map met OCR-traineddata | _(leeg; gebruik de standaard-CDN van Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_AVAILABLE_LANGUAGES` | OCR-talen in de interface, gescheiden door komma's | _(leeg; toon de volledige catalogus)_ |
|
||||
| `VITE_OCR_FONT_BASE_URL` | Map met OCR-tekstlaaglettertypen | _(leeg; gebruik externe Noto-lettertype-URL's)_ |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standaardtaal van de interface | `en` |
|
||||
| `VITE_BRAND_NAME` | Aangepaste merknaam | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logopad relatief aan `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Aangepaste footer-/copyrighttekst | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | Tool-ID's om te verbergen, gescheiden door komma's | _(leeg; alle tools ingeschakeld)_ |
|
||||
| `DISABLE_GITHUB_STARS` | Sla het ophalen van het aantal GitHub-sterren bij het laden van de pagina over en haal `api.github.com` uit de CSP. Builds in Simple Mode slaan het altijd over. | `false` |
|
||||
| `SITE_URL` | Openbare URL die in canonical-, hreflang- en sitemaplinks wordt geschreven. Laat de standaardwaarde staan, tenzij zoekmachines je implementatie als eigen site moeten zien. | `https://www.bentopdf.com` |
|
||||
| `ALLOW_INDEXING` | Bij een aangepaste `SITE_URL` krijgt elke pagina `noindex`, zodat je kopie niet concurreert met bentopdf.com in zoekresultaten. Zet op `true` om indexering toe te staan. | `false` |
|
||||
|
||||
WASM-module-URL's zijn vooraf geconfigureerd met CDN-standaardwaarden, dus alle geavanceerde functies werken direct. Overschrijf ze voor air-gapped of zelfgehoste implementaties.
|
||||
|
||||
### Zoekmachines
|
||||
|
||||
Standaard verwijst elke pagina zoekmachines naar bentopdf.com als origineel, zodat je implementatie niet als duplicaat verschijnt. Voor een privé- of interne instantie hoef je niets te wijzigen.
|
||||
|
||||
- **Houd je instantie helemaal uit zoekresultaten:** start de container met `-e ROBOTS_NOINDEX=true`. Hiervoor is alleen een herstart nodig, geen nieuwe build.
|
||||
- **Maak van je instantie een eigen site in zoekresultaten:** bouw met `--build-arg SITE_URL=https://your-domain.example --build-arg ALLOW_INDEXING=true`. Zonder `ALLOW_INDEXING=true` markeert een aangepaste `SITE_URL` elke pagina als `noindex`.
|
||||
|
||||
### Content-Security-Policy
|
||||
|
||||
De nginx-image wordt geleverd met een afdwingende `Content-Security-Policy`-header. De richtlijnen `script-src`, `connect-src` en `font-src` van de CSP worden **tijdens de build gegenereerd** uit de bovenstaande `VITE_*`-URL-variabelen: alle hosts die je via `--build-arg` meegeeft, worden automatisch aan het beleid toegevoegd.
|
||||
|
||||
Daardoor geldt:
|
||||
|
||||
- Overschrijf je `VITE_CORS_PROXY_URL` of `VITE_WASM_*_URL` tijdens de build, dan staat de CSP die origins automatisch toe, zonder extra configuratie.
|
||||
- Voor URL's op `cdn.jsdelivr.net` staat `script-src` alleen de exacte map van het versiegebonden pakket toe, bijvoorbeeld `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/`, niet de hele CDN. jsDelivr serveert elk openbaar npm-pakket of GitHub-repository, dus het toestaan van de hele host zou elk daarvan op je instantie laten draaien. URL's op andere hosts worden op origin toegestaan.
|
||||
- Configureer je aangepaste WASM-URL's tijdens _runtime_ via de pagina Geavanceerde instellingen in de app, dan staan die origins **niet** in de CSP en blokkeert de browser het ophalen ervan. Dit geldt ook voor een andere versie van hetzelfde pakket op `cdn.jsdelivr.net`, omdat alleen de tijdens de build ingestelde versie is toegestaan. Runtimeconfiguratie is bedoeld om mee te experimenteren; stel voor permanente aangepaste URL's het bijbehorende `VITE_*`-buildargument in.
|
||||
- Air-gapped implementaties die alle drie de `VITE_WASM_*_URL`-waarden overschrijven, verwijderen ook die `cdn.jsdelivr.net`-pakketmappen uit de CSP (elke standaardwaarde wordt vervangen, niet aangevuld). De Tesseract OCR-standaardwaarden op `cdn.jsdelivr.net` blijven in het beleid staan totdat je ook de drie `VITE_TESSERACT_*`-waarden instelt. Zo vervangt het instellen van `VITE_CORS_PROXY_URL` ook de openbare standaardwaarde `bentopdf-cors-proxy.bentopdf.workers.dev`.
|
||||
|
||||
De CSP bevat `'unsafe-eval'` in `script-src`, omdat de LibreOffice WASM-runtime (gebruikt door de conversietools voor Word/Excel/PowerPoint) en de afbeeldingsbibliotheek wasm-vips (gebruikt door PDF naar TIFF) interne dispatch-code compileren via `new Function()`. Zonder dit werken die tools niet meer. Bouw je in `SIMPLE_MODE` (zonder LibreOffice) en heb je PDF naar TIFF niet nodig, dan kun je het gegenereerde `security-headers.conf` handmatig bewerken om `'unsafe-eval'` te verwijderen voor een strenger beleid.
|
||||
|
||||
Laat voor OCR de `VITE_TESSERACT_*`-variabelen leeg om de standaard online assets te gebruiken, of stel alle drie samen in voor zelfgehoste/offline OCR. Gedeeltelijke OCR-overschrijvingen worden geweigerd, omdat de worker, de core-runtime en de traineddata-map bij elkaar moeten passen. Stel voor volledig offline doorzoekbare PDF-uitvoer ook `VITE_OCR_FONT_BASE_URL` in, zodat de OCR-tekstlaaglettertypen van je interne server worden geladen in plaats van van de openbare Noto-lettertype-URL's.
|
||||
|
||||
`VITE_DEFAULT_LANGUAGE` stelt de interfacetaal in voor nieuwe bezoekers. Ondersteunde waarden: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Gebruikers kunnen nog steeds van taal wisselen: dit verandert alleen de standaardtaal.
|
||||
|
||||
Voorbeeld:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Eigen huisstijl
|
||||
|
||||
Vervang het standaardlogo, de naam en de footertekst van BentoPDF door je eigen. Plaats je logobestand in de map `public/` (of gebruik een bestaande afbeelding) en geef de huisstijlvariabelen tijdens de build mee:
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
Huisstijl werkt in zowel de volledige modus als Simple Mode en kan worden gecombineerd met alle andere buildopties.
|
||||
|
||||
### Specifieke tools uitschakelen
|
||||
|
||||
Verberg tools in de interface voor compliance- of beveiligingseisen. Uitgeschakelde tools verdwijnen van de startpagina, uit zoekresultaten, sneltoetsen en de workflowbouwer. Rechtstreekse toegang via de URL toont een pagina "tool niet beschikbaar".
|
||||
|
||||
Tool-ID's zijn de pagina-URL zonder `.html`. Staat een tool bijvoorbeeld op `bentopdf.com/edit-pdf.html`, dan is de ID `edit-pdf`.
|
||||
|
||||
#### Tool-ID's vinden
|
||||
|
||||
Het makkelijkst: open een tool in BentoPDF en kijk naar de URL. Het laatste deel van het pad (zonder `.html`) is de tool-ID.
|
||||
|
||||
<details>
|
||||
<summary>Volledige lijst met tool-ID's</summary>
|
||||
|
||||
**Bewerken en annoteren:** `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`
|
||||
|
||||
**Naar PDF converteren:** `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`
|
||||
|
||||
**Van PDF converteren:** `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`
|
||||
|
||||
**Organiseren en beheren:** `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`
|
||||
|
||||
**Optimaliseren en repareren:** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Beveiliging:** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Optie 1: Tijdens de build (Docker-buildargument)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
Hiermee wordt de lijst met uitgeschakelde tools in de JavaScript-bundel ingebakken. Wijzigen vereist een nieuwe build.
|
||||
|
||||
#### Optie 2: Tijdens runtime (config.json)
|
||||
|
||||
Koppel een bestand `config.json` in de aangeboden map, zonder nieuwe build:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Of met 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 kunnen worden gecombineerd: de lijsten worden samengevoegd. Staat een tool in een van beide, dan is hij uitgeschakeld.
|
||||
|
||||
#### Editorfuncties uitschakelen
|
||||
|
||||
Je kunt ook specifieke functies in de PDF-editor (bijv. redactie, annotaties, formulieren) uitschakelen zonder de hele editortool uit te schakelen. Voeg `editorDisabledCategories` toe aan je `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"editorDisabledCategories": ["redaction", "annotation-stamp"]
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Volledige lijst met editorcategorieën</summary>
|
||||
|
||||
**Zoom:** `zoom`, `zoom-in`, `zoom-out`, `zoom-fit-page`, `zoom-fit-width`, `zoom-marquee`, `zoom-level`
|
||||
|
||||
**Annotatie:** `annotation`, `annotation-markup`, `annotation-highlight`, `annotation-underline`, `annotation-strikeout`, `annotation-squiggly`, `annotation-ink`, `annotation-text`, `annotation-stamp`
|
||||
|
||||
**Vormen:** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
|
||||
|
||||
**Formulier:** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
|
||||
|
||||
**Redactie:** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
|
||||
|
||||
**Document:** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
|
||||
|
||||
**Pagina:** `page`, `spread`, `rotate`, `scroll`, `navigation`
|
||||
|
||||
**Paneel:** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
|
||||
|
||||
**Tools:** `tools`, `pan`, `pointer`, `capture`
|
||||
|
||||
**Selectie:** `selection`, `selection-copy`
|
||||
|
||||
**Geschiedenis:** `history`, `history-undo`, `history-redo`
|
||||
|
||||
</details>
|
||||
|
||||
Categorieën zijn hiërarchisch: het uitschakelen van een bovenliggende categorie (bijv. `annotation`) schakelt alle onderliggende uit.
|
||||
|
||||
### De GitHub-sterrenteller uitschakelen
|
||||
|
||||
De navigatiebalk toont een live aantal GitHub-sterren, bij het laden van de pagina opgehaald van `api.github.com`. Het is het enige externe verzoek dat geen PDF-functie dient. Schakel het uit tijdens de build om de ophaalcode uit de bundel te verwijderen en `api.github.com` uit de Content-Security-Policy te halen:
|
||||
|
||||
```bash
|
||||
docker build --build-arg DISABLE_GITHUB_STARS=true -t bentopdf .
|
||||
```
|
||||
|
||||
Builds in Simple Mode (`bentopdf-simple`) slaan de sterrenteller altijd over: deze vlag is alleen nodig voor de commerciële build of aangepaste volledige builds.
|
||||
|
||||
### Aangepaste WASM-URL's (air-gapped / zelfgehost)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> WASM-URL's worden tijdens de **build** in de JavaScript ingebakken. De WASM-bestanden worden tijdens runtime door de **browser van de gebruiker** gedownload: Docker downloadt ze niet tijdens de build. Voor air-gapped netwerken moet je de WASM-bestanden hosten op een interne server die browsers kunnen bereiken.
|
||||
|
||||
**Volledige air-gapped werkwijze:**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Gebruik de codes die `bash scripts/prepare-airgap.sh --list-ocr-languages` toont, of zoek op naam met `bash scripts/prepare-airgap.sh --search-ocr-language <term>`, voor `--ocr-languages`. Bouw je met een beperkte OCR-subset, geef dan dezelfde codes door aan `VITE_TESSERACT_AVAILABLE_LANGUAGES`, zodat de app alleen meegeleverde talen toont. Host voor volledig offline OCR-uitvoer ook de meegeleverde map `ocr-fonts/` en laat `VITE_OCR_FONT_BASE_URL` ernaar verwijzen.
|
||||
|
||||
Stel een variabele in op een lege string om die module uit te schakelen (gebruikers moeten dan handmatig configureren via Geavanceerde instellingen).
|
||||
|
||||
## Aangepaste poort
|
||||
|
||||
Standaard luistert BentoPDF op poort `8080` in de container. Om dit te wijzigen, stel je tijdens runtime de omgevingsvariabele `PORT` in:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------- | ---------------------------------- | ------- |
|
||||
| `PORT` | Nginx-luisterpoort in de container | `8080` |
|
||||
|
||||
Dit werkt met zowel de standaard als de nonroot Dockerfile.
|
||||
|
||||
## Aangepaste gebruikers-ID (PUID/PGID)
|
||||
|
||||
Voor omgevingen die als een specifieke niet-rootgebruiker moeten draaien (NAS-apparaten, Kubernetes met security contexts, organisatiebeleid) biedt BentoPDF een aparte Dockerfile met PUID/PGID-ondersteuning in LSIO-stijl.
|
||||
|
||||
### Bouwen en uitvoeren
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Omgevingsvariabelen
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------- | ------------------------------- | ------- |
|
||||
| `PUID` | Gebruikers-ID om mee te draaien | `1000` |
|
||||
| `PGID` | Groeps-ID om mee te draaien | `1000` |
|
||||
| `PORT` | Nginx-luisterpoort | `8080` |
|
||||
| `DISABLE_IPV6` | IPv6-listener uitschakelen | `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
|
||||
```
|
||||
|
||||
### Hoe het werkt
|
||||
|
||||
De container start als root, maakt een gebruiker aan met de opgegeven PUID/PGID, past het eigendom van alle beschrijfbare mappen aan en laat daarna rechten vallen met `su-exec`. Het nginx-proces draait volledig als de door jou opgegeven gebruiker.
|
||||
|
||||
> [!NOTE]
|
||||
> De standaard `Dockerfile` gebruikt `nginx-unprivileged` (UID 101) en wordt aanbevolen voor de meeste implementaties. Gebruik `Dockerfile.nonroot` alleen als je een specifieke UID/GID nodig hebt.
|
||||
|
||||
> [!WARNING]
|
||||
> PUID/PGID mag niet `0` (root) zijn. De entrypoint valideert de invoer en stopt met een foutmelding bij ongeldige waarden.
|
||||
|
||||
## Met 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
|
||||
```
|
||||
|
||||
## Met 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"
|
||||
}
|
||||
```
|
||||
|
||||
## Resourcelimieten
|
||||
|
||||
```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-integratie)
|
||||
|
||||
[Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) laat je Podman-containers als systemd-services draaien. Dit is ideaal voor productie-implementaties op Linux-systemen.
|
||||
|
||||
### Basisinstallatie van Quadlet
|
||||
|
||||
Maak een containerunitbestand aan op `~/.config/containers/systemd/bentopdf.container` (gebruiker) of `/etc/containers/systemd/bentopdf.container` (systeem):
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Inschakelen en 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]
|
||||
> Gebruik voor een systeembrede implementatie `systemctl` zonder de vlag `--user` en plaats het bestand in `/etc/containers/systemd/`.
|
||||
|
||||
### Quadlet met 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
|
||||
```
|
||||
|
||||
### Automatisch bijwerken met Quadlet
|
||||
|
||||
Podman kan containers automatisch bijwerken wanneer nieuwe images beschikbaar zijn:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Netwerkconfiguratie van Quadlet
|
||||
|
||||
Maak voor een aangepaste netwerkconfiguratie een netwerkbestand `bentopdf.network` aan:
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Subnet=10.89.0.0/24
|
||||
Gateway=10.89.0.1
|
||||
```
|
||||
|
||||
Verwijs er daarna naar in je containerbestand:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
Network=bentopdf.network
|
||||
```
|
||||
|
||||
## Bijwerken
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Recreate container
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Implementeren op Hostinger
|
||||
|
||||
[Hostinger](https://hostinger.com) is een populaire aanbieder van shared hosting. Deze gids behandelt het implementeren van BentoPDF op de shared hosting van Hostinger.
|
||||
|
||||
## Vereisten
|
||||
|
||||
- Hostinger-hostingpakket met toegang tot de bestandsbeheerder
|
||||
- Lokaal geïnstalleerde Node.js om te bouwen
|
||||
|
||||
## Stap 1: Bouw het project
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
De gebouwde bestanden staan in de map `dist`.
|
||||
|
||||
## Stap 2: Uploaden naar Hostinger
|
||||
|
||||
### Implementatie op het hoofddomein
|
||||
|
||||
1. Log in op je Hostinger-account
|
||||
2. Ga naar **File Manager** → **public_html**
|
||||
3. **Verwijder** bestaande bestanden (maak zo nodig een back-up)
|
||||
4. **Upload** de volledige inhoud van je lokale map `dist` naar `public_html`
|
||||
|
||||
### Implementatie in een submap
|
||||
|
||||
Bij implementatie in een submap (bijv. `yourdomain.com/pdf-tools/`):
|
||||
|
||||
1. Bouw met de juiste basis-URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
2. Maak de map aan in Hostinger:
|
||||
- Ga naar **File Manager** → **public_html**
|
||||
- Maak een nieuwe map: `pdf-tools`
|
||||
3. Upload de volledige inhoud van `dist` naar `public_html/pdf-tools/`
|
||||
|
||||
## Stap 3: Maak het .htaccess-bestand aan
|
||||
|
||||
Maak een bestand `.htaccess` aan in de hoofdmap van je implementatiemap (`public_html` of `public_html/pdf-tools/`):
|
||||
|
||||
::: warning Belangrijk
|
||||
Wijzig bij implementatie in een submap `RewriteBase /` naar `RewriteBase /pdf-tools/` (of de naam van je map).
|
||||
:::
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Voorbeeld van .htaccess voor een submap
|
||||
|
||||
Pas voor `yourdomain.com/pdf-tools/` deze regels aan:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### WASM-bestanden worden niet geladen
|
||||
|
||||
Controleer of de MIME-typen correct zijn ingesteld in `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### LibreOffice-tools werken niet
|
||||
|
||||
De beveiligingsheaders zijn cruciaal voor SharedArrayBuffer:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
```
|
||||
|
||||
Als de headers niet worden toegepast, neem dan contact op met de support van Hostinger om `mod_headers` in te schakelen.
|
||||
|
||||
### 404-fouten bij het vernieuwen van de pagina
|
||||
|
||||
Controleer of de regel voor de extensie `.html` en de taalroutes correct zijn geconfigureerd. BentoPDF gebruikt statische HTML-bestanden, geen 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]
|
||||
```
|
||||
|
||||
### Limieten voor bestandsuploads
|
||||
|
||||
Hostinger kan limieten voor bestandsgrootte hebben. Upload WASM-bestanden afzonderlijk als de bulkupload mislukt.
|
||||
@@ -0,0 +1,538 @@
|
||||
# Zelf hosten
|
||||
|
||||
BentoPDF kun je op je eigen infrastructuur zelf hosten. Deze gids behandelt verschillende implementatiemogelijkheden.
|
||||
|
||||
## Snel aan de slag met Docker / Podman
|
||||
|
||||
De snelste manier om BentoPDF zelf te hosten:
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF wordt in twee builds geleverd:
|
||||
>
|
||||
> - **Self-Hosted build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Alle PDF-tools, **zonder** de marketingelementen (geen hero, FAQ, testimonials, footer). Gebruik dit voor interne implementaties voor teams of organisaties. Het is **geen** versie met minder functies of een "lite"-versie.
|
||||
> - **Commercial build**: `ghcr.io/alam00000/bentopdf:latest`. De volledige marketingsite, gebruikt door bentopdf.com zelf en door commerciële licentiehouders met openbare implementaties.
|
||||
>
|
||||
> Twijfel je? Haal dan de Self-Hosted build op.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Voor het converteren van Office-bestanden is `SharedArrayBuffer` nodig. De app moet daarvoor zowel cross-origin geïsoleerd zijn als vanuit een veilige context worden aangeboden. De officiële image stuurt de vereiste COOP/COEP-headers al mee, maar browsers schakelen `SharedArrayBuffer` nog steeds uit op gewone HTTP-origins in een lokaal netwerk, zoals `http://192.168.x.x`.
|
||||
>
|
||||
> Gebruik `http://localhost` alleen voor tests op hetzelfde apparaat. Als gebruikers BentoPDF via een LAN-IP of hostnaam openen, zorg dan voor 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
|
||||
```
|
||||
|
||||
Of met 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)
|
||||
|
||||
Draai BentoPDF als systemd-service. Maak `~/.config/containers/systemd/bentopdf.container` aan:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Zie de [Docker-implementatiegids](/nl/self-hosting/docker) voor de volledige Quadlet-documentatie.
|
||||
|
||||
## Bouwen vanuit de broncode
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Configuratieopties
|
||||
|
||||
### Self-Hosted build (Simple Mode)
|
||||
|
||||
De Self-Hosted build (de `bentopdf-simple` image, ook wel Simple Mode genoemd) is **functioneel identiek** aan de Commercial build: alle PDF-tools zijn aanwezig en werken hetzelfde. Hij verbergt alleen de marketingelementen die uitsluitend zinvol zijn op bentopdf.com zelf of bij een commerciële, openbare implementatie. **Het is geen versie met minder functies of een "lite"-versie.**
|
||||
|
||||
**Wat de Self-Hosted build verbergt** (alleen cosmetisch, er worden geen PDF-functies verwijderd):
|
||||
|
||||
- Navigatiebalk, hero-sectie, functiesectie, FAQ, testimonials, footer
|
||||
- Wijzigt de paginatitel in "PDF-tools"
|
||||
|
||||
**Wat de Self-Hosted build behoudt** (alles wat daadwerkelijk PDF-werk doet):
|
||||
|
||||
- Alle PDF-tools (samenvoegen, splitsen, bewerken, ondertekenen, OCR, Office-conversie, enz.)
|
||||
- Ondersteuning voor eigen huisstijl, alle configuratie tijdens het bouwen en tijdens runtime
|
||||
|
||||
De Commercial build (`ghcr.io/alam00000/bentopdf:latest`) is wat bentopdf.com zelf draait en wordt gebruikt door commerciële licentiehouders met openbare implementaties. Hij voegt de hero, FAQ, testimonials en footer toe die op een interne tool niet zinvol zouden zijn.
|
||||
|
||||
Host je BentoPDF zelf voor je team, organisatie of als interne tool, haal dan de Self-Hosted build op:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Zie [SIMPLE_MODE.md](https://github.com/alam00000/bentopdf/blob/main/SIMPLE_MODE.md) voor alle details.
|
||||
|
||||
### Commercial build (openbare implementaties met je eigen merk)
|
||||
|
||||
De Commercial build (de `bentopdf` image, zonder het achtervoegsel `-simple`) is wat bentopdf.com zelf draait. Hij bevat de volledige marketingsite (hero, functies, FAQ, testimonials, footer) bovenop alle PDF-tools. Gebruik deze build als je **BentoPDF wilt implementeren als openbare PDF-dienst onder je eigen merk**, bijvoorbeeld:
|
||||
|
||||
- Je draait BentoPDF als gehoste SaaS voor eindgebruikers op je eigen domein
|
||||
- Je wilt de ervaring van de landingspagina (marketingsecties plus tools), niet alleen de kale toolomgeving
|
||||
- Je bent commercieel licentiehouder en verwerkt BentoPDF in een commercieel product
|
||||
|
||||
**Draai hem ongewijzigd** (met BentoPDF-huisstijl, handig om de build te bekijken):
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
**Bouw met je eigen merk** (de gebruikelijke commerciële werkwijze):
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Elke andere buildoptie (`BASE_URL`, `VITE_DEFAULT_LANGUAGE`, `DISABLE_TOOLS`, WASM-URL-overschrijvingen, enz.) werkt op dezelfde manier als bij de Self-Hosted build.
|
||||
|
||||
::: warning Licenties
|
||||
De Commercial build draaien is toegestaan onder een van de twee licentieopties van BentoPDF:
|
||||
|
||||
- **AGPL-3.0** (gratis): toegestaan als je implementatie de volledige broncode onder AGPL publiceert, inclusief aanpassingen aan de huisstijl en de omringende bedrijfslogica.
|
||||
- **Commerciële licentie** ($79 eenmalig): vereist voor closed-source / propriëtaire implementaties waarbij je je huisstijlfork of bedrijfscode niet open source maakt.
|
||||
|
||||
Zie de [Licentiepagina](https://www.bentopdf.com/licensing) voor de volledige vergelijking. AGPL-gelicentieerde WASM-modules (PyMuPDF, Ghostscript, CoherentPDF) worden tijdens runtime vanaf een CDN geladen, dus ze komen niet in je image terecht en veranderen je licentiepositie niet.
|
||||
:::
|
||||
|
||||
### Basis-URL
|
||||
|
||||
Implementeren in een submap:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
### Eigen huisstijl
|
||||
|
||||
Vervang tijdens het bouwen het standaard BentoPDF-logo, de naam en de voettekst door je eigen:
|
||||
|
||||
| Variabele | Beschrijving | Standaard |
|
||||
| ------------------ | ------------------------------------------- | --------------------------------------- |
|
||||
| `VITE_BRAND_NAME` | Merknaam in de header en footer | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logopad relatief ten opzichte van `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Eigen footer-/copyrighttekst | `© 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
|
||||
```
|
||||
|
||||
Of via 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 .
|
||||
```
|
||||
|
||||
Eigen huisstijl werkt in zowel de volledige modus als Simple Mode en kan worden gecombineerd met alle andere buildopties (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Specifieke tools uitschakelen
|
||||
|
||||
Verberg afzonderlijke tools voor compliance of beveiliging. Uitgeschakelde tools worden verwijderd van de startpagina, uit zoeken, sneltoetsen, de workflowbouwer en directe URL-toegang.
|
||||
|
||||
Tool-ID's zijn de pagina-URL zonder `.html`: open een tool en kijk naar de URL (bijv. `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Tijdens het bouwen** (ingebakken in de bundel):
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**Tijdens runtime** (geen nieuwe build nodig: koppel een `config.json`):
|
||||
|
||||
```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 kunnen worden gecombineerd: de lijsten worden samengevoegd.
|
||||
|
||||
Je kunt ook specifieke functies in de PDF-editor (bijv. redactie) uitschakelen zonder de hele tool uit te schakelen door `editorDisabledCategories` toe te voegen aan `config.json`. Zie de [Docker-gids](/nl/self-hosting/docker#editorfuncties-uitschakelen) voor de volledige lijst met categorieën.
|
||||
|
||||
## Implementatiegidsen
|
||||
|
||||
Kies je platform:
|
||||
|
||||
- [Vercel](/nl/self-hosting/vercel)
|
||||
- [Netlify](/nl/self-hosting/netlify)
|
||||
- [Cloudflare Pages](/nl/self-hosting/cloudflare)
|
||||
- [AWS S3 + CloudFront](/nl/self-hosting/aws)
|
||||
- [Hostinger](/nl/self-hosting/hostinger)
|
||||
- [Nginx](/nl/self-hosting/nginx)
|
||||
- [Apache](/nl/self-hosting/apache)
|
||||
- [Docker](/nl/self-hosting/docker)
|
||||
- [Kubernetes](/nl/self-hosting/kubernetes)
|
||||
- [CORS Proxy](/nl/self-hosting/cors-proxy) - Vereist voor digitale handtekeningen
|
||||
|
||||
## Veelvoorkomende problemen
|
||||
|
||||
### Word / ODT / Excel / PowerPoint naar PDF blijft hangen (SharedArrayBuffer niet beschikbaar)
|
||||
|
||||
**Symptoom**: Documentconversies op basis van LibreOffice (Word, ODT, Excel, PowerPoint naar PDF) blijven hangen bij ~55% of starten niet. De browserconsole toont mogelijk:
|
||||
|
||||
> `ReferenceError: SharedArrayBuffer is not defined`
|
||||
|
||||
...of `window.crossOriginIsolated` meldt `false`, of de WASM-compilatie meldt `expected magic word 00 61 73 6d, found 1f 8b 08 08`.
|
||||
|
||||
**Oorzaak**: LibreOffice WASM vereist `SharedArrayBuffer`, dat de browser alleen inschakelt wanneer de pagina **cross-origin geïsoleerd** is EN vanuit een **veilige context** wordt aangeboden. Dat betekent dat twee dingen moeten kloppen:
|
||||
|
||||
1. Elke respons bevat beide headers:
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
2. De pagina wordt aangeboden vanaf `https://...` of `http://localhost`. Gewone HTTP op een LAN-IP (bijv. `http://192.168.x.x`) telt NIET als veilig: browsers schakelen `SharedArrayBuffer` daar uit.
|
||||
|
||||
Het verschil tussen `00 61 73 6d / 1f 8b 08 08` is een apart subsymptoom: bij de vooraf gecomprimeerde `.wasm.gz` / `.data.gz`-bestanden ontbreekt de responsheader `Content-Encoding: gzip`, waardoor de browser ruwe gzip-bytes ontvangt in plaats van gedecomprimeerde WASM.
|
||||
|
||||
**Oplossing**: zie de platformspecifieke implementatiegids voor de exacte configuratie:
|
||||
|
||||
- [Nginx →](/nl/self-hosting/nginx#pdf-ondertekenen-of-formulierinvuller-toont-een-lege-viewer-mjs-mime-fout)
|
||||
- [Apache →](/nl/self-hosting/apache#pdf-ondertekenen-of-formulierinvuller-toont-een-leeg-weergavevenster-mjs-mime-fout)
|
||||
- [AWS S3 + CloudFront →](/nl/self-hosting/aws#stap-3b-response-headers-policy-vereist-voor-libreoffice-wasm)
|
||||
- [Cloudflare Pages →](/nl/self-hosting/cloudflare#configuratiebestand) (`_headers`-bestand)
|
||||
- [Netlify →](/nl/self-hosting/netlify#word-odt-excel-naar-pdf-blijft-hangen-op-55) (`netlify.toml`)
|
||||
- [Vercel →](/nl/self-hosting/vercel#word-odt-excel-naar-pdf-werkt-niet) (`vercel.json`)
|
||||
- [Hostinger →](/nl/self-hosting/hostinger#libreoffice-tools-werken-niet) (`.htaccess`)
|
||||
- [Kubernetes →](/nl/self-hosting/kubernetes#mjs-mime-typefouten-iframe-van-pdf-ondertekenen-pdf-formulier-invullen-leeg)
|
||||
- **Docker**: wordt automatisch afgehandeld door de meegeleverde nginx-configuratie, geen actie nodig.
|
||||
|
||||
**Controleren**: open de DevTools-console op een willekeurige BentoPDF-pagina en voer uit:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Als de pagina HTTPS of `http://localhost` gebruikt EN beide COEP/COOP-headers op elke respons aanwezig zijn, slagen beide controles. Zit je op `http://192.168.x.x` of een andere niet-loopback HTTP-origin, zorg dan voor HTTPS: een oplossing met alleen headers bestaat niet.
|
||||
|
||||
### `.mjs`-bestanden geleverd als `application/octet-stream`
|
||||
|
||||
**Symptoom**: PDF ondertekenen / PDF-formulier invullen / enkele andere tools tonen een lege viewer of laden niet. De browserconsole meldt:
|
||||
|
||||
> `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.`
|
||||
|
||||
**Oorzaak**: Je webserver of reverse proxy heeft geen MIME-type-koppeling voor `.mjs`-bestanden (de meegeleverde PDF-viewer gebruikt ES-modules met die extensie). Veel standaard serverconfiguraties gebruiken voor onbekende extensies `application/octet-stream`, wat browsers weigeren uit te voeren als ES-modules.
|
||||
|
||||
**Oplossing**: zie de platformspecifieke implementatiegids voor het exacte fragment:
|
||||
|
||||
- [Nginx →](/nl/self-hosting/nginx#pdf-ondertekenen-of-formulierinvuller-toont-een-lege-viewer-mjs-mime-fout)
|
||||
- [Apache →](/nl/self-hosting/apache#pdf-ondertekenen-of-formulierinvuller-toont-een-leeg-weergavevenster-mjs-mime-fout)
|
||||
- [AWS S3 + CloudFront →](/nl/self-hosting/aws#stap-2-bouwen-en-uploaden) (zie `aws s3 cp ... --include "*.mjs"`)
|
||||
- [Kubernetes →](/nl/self-hosting/kubernetes#mjs-mime-typefouten-iframe-van-pdf-ondertekenen-pdf-formulier-invullen-leeg)
|
||||
- **Docker, Vercel, Netlify, Cloudflare Pages, Hostinger**: worden automatisch afgehandeld, geen actie nodig.
|
||||
|
||||
**Controleren**: open DevTools → tabblad Netwerk, zoek het mislukte `.mjs`-verzoek en bekijk de responsheader `Content-Type`. Die hoort `application/javascript` te zijn. Is het nog steeds `application/octet-stream`, dan bepaalt een externe reverse proxy of CDN het type mogelijk opnieuw: controleer elke laag in je serveringsketen.
|
||||
|
||||
## WASM-configuratie (AGPL-componenten)
|
||||
|
||||
BentoPDF **bundelt geen** AGPL-gelicentieerde verwerkingsbibliotheken in de broncode, maar **stelt CDN-URL's vooraf in** zodat alle functies direct werken, zonder handmatige configuratie.
|
||||
|
||||
::: tip Standaard zonder configuratie
|
||||
Sinds v2.0.0 zijn WASM-modules via omgevingsvariabelen vooraf ingesteld om vanaf de jsDelivr CDN te laden. Alle geavanceerde functies werken direct zonder configuratie door de gebruiker.
|
||||
:::
|
||||
|
||||
| Component | Licentie | Functies |
|
||||
| --------------- | -------- | ----------------------------------------------------------------- |
|
||||
| **PyMuPDF** | AGPL-3.0 | EPUB/MOBI/FB2/XPS-conversie, afbeeldingsextractie, tabelextractie |
|
||||
| **Ghostscript** | AGPL-3.0 | PDF/A-conversie, compressie, rechtzetten, rasteren |
|
||||
| **CoherentPDF** | AGPL-3.0 | Inhoudsopgave, bijlagen, PDF samenvoegen met bladwijzers |
|
||||
|
||||
### Standaard omgevingsvariabelen
|
||||
|
||||
Deze staan in `.env.production` en worden in de build ingebakken:
|
||||
|
||||
```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-URL's overschrijven
|
||||
|
||||
Je kunt de standaardwaarden tijdens het bouwen overschrijven voor aangepaste implementaties:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Om een module helemaal uit te schakelen (handmatige configuratie door de gebruiker via Geavanceerde instellingen is dan vereist), stel je de variabele in op een lege tekenreeks.
|
||||
|
||||
Laat voor OCR alle `VITE_TESSERACT_*`-variabelen leeg en gebruik de standaard online bronnen, of stel de worker-, core- en lang-URL's samen in voor zelf gehoste/offline OCR. Bundel je alleen specifieke OCR-talen, stel dan ook `VITE_TESSERACT_AVAILABLE_LANGUAGES` in op dezelfde kommagescheiden codes, zodat de interface alleen geïnstalleerde talen aanbiedt en niet-ondersteunde selecties mislukken met een duidelijke foutmelding. Stel voor volledig offline doorzoekbare PDF-uitvoer ook `VITE_OCR_FONT_BASE_URL` in op de interne map waarin de meegeleverde OCR-lettertypen worden aangeboden.
|
||||
|
||||
Gebruikers kunnen deze standaardwaarden ook op elk moment overschrijven via **Geavanceerde instellingen** in de interface. Overschrijvingen van gebruikers die in de browser zijn opgeslagen, hebben voorrang op de standaardwaarden uit de omgeving.
|
||||
|
||||
### Air-gapped / offline implementatie
|
||||
|
||||
Voor netwerken zonder internettoegang (overheid, zorg, financiële sector, enz.). De WASM-URL's worden tijdens het **bouwen** in de JavaScript ingebakken: de eigenlijke WASM-bestanden worden tijdens runtime door de **browser van de gebruiker** gedownload. Je moet dus alles voorbereiden op een machine met internet en het daarna overbrengen naar het geïsoleerde netwerk.
|
||||
|
||||
#### Geautomatiseerd script (aanbevolen)
|
||||
|
||||
Het meegeleverde script `prepare-airgap.sh` automatiseert het hele proces: het downloadt WASM-pakketten, bouwt de Docker-image en maakt een zelfstandige bundel met een installatiescript.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Dit levert een bundelmap op:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Breng de bundel via USB, een interne artefactrepository of een goedgekeurde methode over naar het air-gapped netwerk. Voer daarna het meegeleverde installatiescript uit:
|
||||
|
||||
```bash
|
||||
cd bentopdf-airgap-bundle
|
||||
bash setup.sh
|
||||
```
|
||||
|
||||
Het installatiescript laadt de Docker-image, pakt de WASM-bestanden uit en start optioneel de container.
|
||||
|
||||
**Scriptopties:**
|
||||
|
||||
| Vlag | Beschrijving | Standaard |
|
||||
| ------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------- |
|
||||
| `--wasm-base-url <url>` | Waar de WASM's intern worden gehost | _(vereist, wordt gevraagd indien niet opgegeven)_ |
|
||||
| `--image-name <name>` | Docker-imagetag | `bentopdf` |
|
||||
| `--output-dir <path>` | Uitvoermap voor de bundel | `./bentopdf-airgap-bundle` |
|
||||
| `--simple-mode` | Simple Mode inschakelen | uit |
|
||||
| `--base-url <path>` | Basis-URL van submap (bijv. `/pdf/`) | `/` |
|
||||
| `--language <code>` | Standaardtaal van de interface (bijv. `fr`, `de`) | _(geen)_ |
|
||||
| `--brand-name <name>` | Eigen merknaam | _(geen)_ |
|
||||
| `--brand-logo <path>` | Logopad relatief ten opzichte van `public/` | _(geen)_ |
|
||||
| `--footer-text <text>` | Eigen voettekst | _(geen)_ |
|
||||
| `--ocr-languages <list>` | Kommagescheiden OCR-talen om te bundelen | `eng` |
|
||||
| `--list-ocr-languages` | Toon ondersteunde OCR-codes en namen en stop | uit |
|
||||
| `--search-ocr-language <term>` | Zoek OCR-codes op naam of afkorting | uit |
|
||||
| `--dockerfile <path>` | Te gebruiken Dockerfile | `Dockerfile` |
|
||||
| `--skip-docker` | Docker-build en export overslaan | uit |
|
||||
| `--skip-wasm` | WASM-download overslaan (bestaande `.tgz`-bestanden hergebruiken) | uit |
|
||||
|
||||
De interactieve prompt accepteert ook `list` om de volledige lijst met ondersteunde Tesseract-codes te tonen en `search <term>` om resultaten te vinden, zoals `search german` of `search chi`.
|
||||
|
||||
::: warning Vereiste van dezelfde origin
|
||||
WASM-bestanden moeten worden aangeboden vanaf dezelfde **origin** als de BentoPDF-app. Web Workers gebruiken `importScripts()`, dat geen scripts cross-origin kan laden. Draait BentoPDF bijvoorbeeld op `https://internal.example.com`, dan moet de WASM-basis-URL ook `https://internal.example.com/wasm` zijn.
|
||||
:::
|
||||
|
||||
#### Handmatige stappen
|
||||
|
||||
<details>
|
||||
<summary>Als je het liever handmatig doet, zonder het script</summary>
|
||||
|
||||
**Stap 1: Download de WASM- en OCR-pakketten** (op een machine met internet)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
**Stap 2: Bouw de Docker-image met interne URL's**
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
**Stap 3: Exporteer de Docker-image**
|
||||
|
||||
```bash
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
```
|
||||
|
||||
**Stap 4: Breng alles over naar het air-gapped netwerk**
|
||||
|
||||
Kopieer via USB, een interne artefactrepository of een goedgekeurde overdrachtsmethode:
|
||||
|
||||
- `bentopdf.tar`: de Docker-image
|
||||
- De vijf `.tgz` WASM-/OCR-pakketten uit stap 1
|
||||
- De map `tesseract-langdata/` uit stap 1
|
||||
- De map `ocr-fonts/` uit stap 1
|
||||
|
||||
**Stap 5: Installeer binnen het air-gapped netwerk**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Zorg dat de bestanden bereikbaar zijn op de URL's die je in stap 2 hebt ingesteld, waaronder `.../ocr/worker.min.js`, `.../ocr/core`, `.../ocr/lang-data` en `.../ocr/fonts`.
|
||||
|
||||
</details>
|
||||
|
||||
::: info Bouwen vanuit de broncode in plaats van Docker?
|
||||
Stel de variabelen in `.env.production` in voordat je `npm run build` uitvoert:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Je eigen WASM-proxy hosten
|
||||
|
||||
Als je AGPL-WASM-bestanden met de juiste CORS-headers wilt aanbieden, kun je een eenvoudige proxy implementeren. Zie de [Cloudflare WASM Proxy-gids](https://github.com/alam00000/bentopdf/blob/main/cloudflare/WASM-PROXY.md) voor een voorbeeldimplementatie.
|
||||
|
||||
::: tip Waarom gescheiden?
|
||||
Deze scheiding zorgt voor:
|
||||
|
||||
- Duidelijke juridische naleving voor commerciële gebruikers
|
||||
- De kern van BentoPDF blijft onder de dubbele licentie (AGPL-3.0 / Commercieel)
|
||||
- WASM-bestanden worden tijdens runtime geladen en niet in de broncode gebundeld
|
||||
:::
|
||||
|
||||
## Systeemvereisten
|
||||
|
||||
| Vereiste | Minimum |
|
||||
| -------- | ---------------------------------- |
|
||||
| Opslag | ~100 MB (kern zonder AGPL-modules) |
|
||||
| RAM | 512 MB |
|
||||
| CPU | Elke moderne processor |
|
||||
|
||||
::: tip
|
||||
BentoPDF is een statische site: er is geen database of backendserver nodig!
|
||||
:::
|
||||
@@ -0,0 +1,211 @@
|
||||
# Implementeren met Kubernetes
|
||||
|
||||
Kubernetes is misschien overdreven voor een statische site, maar het kan een goede keuze zijn als je al standaardiseert op Helm + GitOps.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Vereiste headers voor Office-bestandsconversie**
|
||||
>
|
||||
> Tools op basis van LibreOffice (conversie van Word, Excel, PowerPoint) vereisen deze HTTP-headers voor `SharedArrayBuffer`-ondersteuning:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> De officiële BentoPDF nginx-images bevatten deze headers. In Kubernetes zijn **Ingress-/Gateway-controllers ook reverse proxies**, dus zorg dat deze headers behouden blijven (of voeg ze toe aan de edge).
|
||||
|
||||
## Vereisten
|
||||
|
||||
- Kubernetes-cluster
|
||||
- Helm v3
|
||||
- Een BentoPDF nginx-image (bijv. `ghcr.io/alam00000/bentopdf:<tag>`) die op **poort 8080** draait
|
||||
|
||||
## Implementeren met Helm
|
||||
|
||||
### Installeren vanuit deze repo (lokale 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
|
||||
```
|
||||
|
||||
### Installeren vanuit GHCR (OCI-chart)
|
||||
|
||||
Als de chart als OCI-artefact op GHCR is gepubliceerd:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Beschikbaar maken
|
||||
|
||||
### Port-forward (snelle test)
|
||||
|
||||
```bash
|
||||
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
|
||||
```
|
||||
|
||||
### Ingress (optioneel)
|
||||
|
||||
Schakel Ingress in (voorbeeld voor nginx-ingress):
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: pdf.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Gateway API (optioneel)
|
||||
|
||||
Deze chart ondersteunt Gateway API `Gateway` + `HTTPRoute`.
|
||||
|
||||
Voorbeeld (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
|
||||
```
|
||||
|
||||
## Zorgen dat de SharedArrayBuffer-headers blijven werken (Ingress/Gateway)
|
||||
|
||||
### Wat er hoort te gebeuren
|
||||
|
||||
De nginx-configuratie van BentoPDF stelt de vereiste responsheaders in. De meeste Ingress-/Gateway-controllers **laten responsheaders van de upstream ongewijzigd door**.
|
||||
|
||||
### Wat het kan breken
|
||||
|
||||
- Een controller- of edge-beleid dat responsheaders **overschrijft** of **verwijdert**
|
||||
- Een "security headers"-middleware die andere COOP/COEP-waarden instelt
|
||||
|
||||
### Hoe te controleren
|
||||
|
||||
Voer dit uit tegen je openbare endpoint:
|
||||
|
||||
```bash
|
||||
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
|
||||
```
|
||||
|
||||
Je zou moeten zien:
|
||||
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### Als je Ingress-controller ze niet behoudt
|
||||
|
||||
Voeg de headers toe aan de edge (afhankelijk van de controller). Voorbeeld voor **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-typefouten (iframe van PDF ondertekenen / PDF-formulier invullen leeg)
|
||||
|
||||
Als de browserconsole `Failed to load module script: ... non-JavaScript MIME type "application/octet-stream"` toont bij een `.mjs`-verzoek, vervangt een Ingress-/Gateway-controller vóór de BentoPDF nginx de upstream `Content-Type`-header door `application/octet-stream`.
|
||||
|
||||
De nginx van de BentoPDF-image levert `.mjs` al als `application/javascript`. De oplossing is te voorkomen dat de controller de header verwijdert of vervangt. Voor nginx-ingress:
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
location ~* \.mjs$ {
|
||||
types {} default_type application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
Of gebruik een `ResponseHeaderModifier`-filter in Gateway API om `Content-Type: application/javascript` in te stellen voor `.mjs`-paden. Controleer met DevTools → tabblad Netwerk: het mislukte `.mjs`-verzoek zou nu `content-type: application/javascript` moeten tonen.
|
||||
|
||||
### Gebruik je Gateway API en wil je headers afdwingen
|
||||
|
||||
Gateway API ondersteunt een `ResponseHeaderModifier`-filter. Je kunt dit koppelen in `httpRoute.rules[*].filters`:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Ondersteuning voor specifieke filters hangt af van je Gateway-controller. Wordt een filter genegeerd, voeg de headers dan toe aan de edge of controllerlaag.
|
||||
|
||||
## Specifieke tools uitschakelen
|
||||
|
||||
Gebruik een ConfigMap om tools tijdens runtime uit te schakelen zonder de image opnieuw te bouwen:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Koppel het in de aangeboden map:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Tool-ID's zijn de pagina-URL zonder `.html`: open een tool en kijk naar de URL (bijv. `edit-pdf`, `merge-pdf`, `compress-pdf`). Uitgeschakelde tools worden verborgen op de startpagina, in zoeken, sneltoetsen, de workflowbouwer en bij directe URL-toegang. Zie de [Docker-gids](/nl/self-hosting/docker#specifieke-tools-uitschakelen) voor de volledige lijst met opties.
|
||||
@@ -0,0 +1,139 @@
|
||||
# Implementeren op Netlify
|
||||
|
||||
[Netlify](https://netlify.com) biedt uitstekende hosting voor statische sites met een royale gratis laag.
|
||||
|
||||
## Implementeren met één klik
|
||||
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Handmatig implementeren
|
||||
|
||||
### Stap 1: Repository koppelen
|
||||
|
||||
1. Log in bij [Netlify](https://app.netlify.com)
|
||||
2. Klik op "Add new site" → "Import an existing project"
|
||||
3. Koppel je GitHub-account
|
||||
4. Selecteer je BentoPDF-fork
|
||||
|
||||
### Stap 2: Buildinstellingen configureren
|
||||
|
||||
| Instelling | Waarde |
|
||||
| ----------------- | --------------- |
|
||||
| Build command | `npm run build` |
|
||||
| Publish directory | `dist` |
|
||||
| Node version | 18+ |
|
||||
|
||||
### Stap 3: Implementeren
|
||||
|
||||
Klik op "Deploy site" en wacht tot de build klaar is.
|
||||
|
||||
## Configuratiebestand
|
||||
|
||||
Maak `netlify.toml` aan in de hoofdmap van je project:
|
||||
|
||||
```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 Belangrijk
|
||||
De headers `Cross-Origin-Embedder-Policy` en `Cross-Origin-Opener-Policy` zijn vereist voor conversies van Word/ODT/Excel/PowerPoint naar PDF. Zonder deze headers is `SharedArrayBuffer` niet beschikbaar en kan de LibreOffice WASM-engine niet worden geïnitialiseerd.
|
||||
|
||||
De `Content-Encoding: gzip`-headers op de bestanden `.wasm.gz` en `.data.gz` zorgen ervoor dat de browser ze automatisch decomprimeert. Zonder deze headers ontvangt de browser ruwe gzip-bytes en mislukt de WASM-compilatie.
|
||||
:::
|
||||
|
||||
## Omgevingsvariabelen
|
||||
|
||||
Stel deze in via Site settings → Environment variables:
|
||||
|
||||
| Variabele | Beschrijving |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| `SIMPLE_MODE` | Stel in op `true` voor een minimale build |
|
||||
| `VITE_BRAND_NAME` | Eigen merknaam (vervangt "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logopad relatief ten opzichte van `public/` (bijv. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Eigen footer-/copyrighttekst |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standaardtaal van de interface (bijv. `fr`, `de`, `es`) |
|
||||
|
||||
## Eigen domein
|
||||
|
||||
1. Ga naar Site settings → Domain management
|
||||
2. Klik op "Add custom domain"
|
||||
3. Volg de DNS-configuratie-instructies
|
||||
|
||||
## Grote media
|
||||
|
||||
Overweeg voor grote WASM-bestanden [Netlify Large Media](https://docs.netlify.com/large-media/overview/) in te schakelen:
|
||||
|
||||
```bash
|
||||
netlify lm:setup
|
||||
git lfs track "*.wasm"
|
||||
```
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### Word/ODT/Excel naar PDF blijft hangen op 55%
|
||||
|
||||
Als documentconversies blijven hangen op 55%, open dan de DevTools-console en controleer:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Als `crossOriginIsolated` `false` is, worden de COEP/COOP-headers uit je `netlify.toml` niet toegepast. Zorg dat het bestand in de hoofdmap van je project staat en implementeer opnieuw.
|
||||
|
||||
Als je `expected magic word 00 61 73 6d, found 1f 8b 08 08` in de console ziet, ontbreken bij de `.wasm.gz`-bestanden de `Content-Encoding: gzip`-headers. Zorg dat de `[[headers]]`-blokken voor `soffice.wasm.gz` en `soffice.data.gz` in je `netlify.toml` staan.
|
||||
|
||||
### Build mislukt
|
||||
|
||||
Controleer de compatibiliteit van de Node-versie:
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
```
|
||||
|
||||
### Trage eerste lading
|
||||
|
||||
Schakel assetoptimalisatie in via Site settings → Build & deploy → Asset optimization.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Implementeren met Nginx
|
||||
|
||||
Host BentoPDF op je eigen server met Nginx.
|
||||
|
||||
## Vereisten
|
||||
|
||||
- Ubuntu/Debian-server
|
||||
- Nginx geïnstalleerd
|
||||
- SSL-certificaat (aanbevolen: Let's Encrypt)
|
||||
|
||||
## Stap 1: Bouw het project
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Om de huisstijl aan te passen, stel je omgevingsvariabelen in vóór het bouwen:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Stap 2: Bestanden kopiëren
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Stap 3: Nginx-configuratie
|
||||
|
||||
Maak `/etc/nginx/sites-available/bentopdf` aan:
|
||||
|
||||
```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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Stap 4: De site inschakelen
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Stap 5: SSL met Let's Encrypt
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
## Implementatie in een submap
|
||||
|
||||
Om te hosten op `/pdf/`:
|
||||
|
||||
```nginx
|
||||
location /pdf/ {
|
||||
alias /var/www/bentopdf/;
|
||||
try_files $uri $uri/ /pdf/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
Bouw met:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
## Prestaties afstellen
|
||||
|
||||
Voeg toe aan `nginx.conf`:
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### WASM wordt niet geladen
|
||||
|
||||
Zorg dat het MIME-type is ingesteld:
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
### PDF ondertekenen of formulierinvuller toont een lege viewer (`.mjs` MIME-fout)
|
||||
|
||||
Als de browserconsole het volgende toont:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
...en het mislukte verzoek eindigt op `.mjs`, dan mist in je nginx de koppeling `.mjs → application/javascript`. De meegeleverde PDF-viewer (gebruikt door PDF ondertekenen, PDF-formulier invullen en een paar andere tools) levert ES-modulebestanden met de extensie `.mjs`, en de standaard `mime.types` van nginx bevat die niet.
|
||||
|
||||
Los dit op door `mjs` toe te voegen aan de bestaande regel `application/javascript` in `/etc/nginx/mime.types`:
|
||||
|
||||
```nginx
|
||||
application/javascript js mjs;
|
||||
```
|
||||
|
||||
> Voeg GEEN _tweede_ regel `application/javascript` toe: nginx leest `mime.types` regel voor regel en slechts één koppeling van type naar extensie wint, dus een tweede regel wordt op sommige versies stilzwijgend genegeerd. Pas de bestaande regel aan door `mjs` naast `js` toe te voegen.
|
||||
|
||||
Of overschrijf het lokaal in je serverblok (geen globale bestandswijziging):
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
Laad nginx opnieuw na de wijziging:
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Om te bevestigen: zoek in browser DevTools → tabblad Netwerk het mislukte `.mjs`-verzoek en controleer de responsheader `Content-Type`. Die moet nu `application/javascript` zijn, niet `application/octet-stream`.
|
||||
|
||||
### Word/ODT/Excel naar PDF werkt niet
|
||||
|
||||
LibreOffice WASM vereist `SharedArrayBuffer`, waarvoor de headers `Cross-Origin-Embedder-Policy` en `Cross-Origin-Opener-Policy` nodig zijn. Ook is een veilige context nodig, dus `http://localhost` werkt voor lokaal testen, maar `http://192.168.x.x` of andere LAN-IP's vereisen meestal HTTPS. Let op: nginx `add_header`-directives in een `location`-blok **overschrijven** `add_header`-directives op serverniveau, ze worden niet samengevoegd. Elk `location`-blok met eigen `add_header` moet de COEP/COOP-headers bevatten.
|
||||
|
||||
Controleren met:
|
||||
|
||||
```bash
|
||||
curl -I https://your-domain.com/ | grep -i cross-origin
|
||||
```
|
||||
|
||||
Gebruik je een reverse proxy vóór nginx, zorg dan dat die deze headers behoudt.
|
||||
|
||||
### 502 Bad Gateway
|
||||
|
||||
Controleer de Nginx-foutlogboeken:
|
||||
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
# Implementeren op Vercel
|
||||
|
||||
[Vercel](https://vercel.com) biedt de eenvoudigste implementatie-ervaring voor statische sites.
|
||||
|
||||
## Implementeren met één klik
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Handmatig implementeren
|
||||
|
||||
### Stap 1: Fork de repository
|
||||
|
||||
Fork [bentopdf/bentopdf](https://github.com/alam00000/bentopdf) naar je GitHub-account.
|
||||
|
||||
### Stap 2: Importeren in Vercel
|
||||
|
||||
1. Ga naar [vercel.com/new](https://vercel.com/new)
|
||||
2. Selecteer je geforkte repository
|
||||
3. Configureer het project:
|
||||
|
||||
| Instelling | Waarde |
|
||||
| ---------------- | --------------- |
|
||||
| Framework Preset | Vite |
|
||||
| Build Command | `npm run build` |
|
||||
| Output Directory | `dist` |
|
||||
| Install Command | `npm install` |
|
||||
|
||||
### Stap 3: Omgevingsvariabelen (optioneel)
|
||||
|
||||
Voeg deze toe indien nodig:
|
||||
|
||||
| Variabele | Beschrijving |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| `SIMPLE_MODE` | Stel in op `true` voor een minimale interface |
|
||||
| `VITE_BRAND_NAME` | Eigen merknaam (vervangt "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logopad relatief ten opzichte van `public/` (bijv. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Eigen footer-/copyrighttekst |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standaardtaal van de interface (bijv. `fr`, `de`, `es`) |
|
||||
|
||||
### Stap 4: Implementeren
|
||||
|
||||
Klik op "Deploy" en wacht tot de build klaar is.
|
||||
|
||||
## Eigen domein
|
||||
|
||||
1. Ga naar de projectinstellingen
|
||||
2. Ga naar "Domains"
|
||||
3. Voeg je eigen domein toe
|
||||
4. Configureer DNS zoals aangegeven
|
||||
|
||||
## Beperkingen
|
||||
|
||||
::: warning Grote bestanden
|
||||
De serverless functions van Vercel hebben een limiet van 50 MB. Omdat BentoPDF een statische site is, zou dit je niet moeten raken, maar WASM-modules zijn groot (~100 MB in totaal). Zorg dat ze worden aangeboden vanuit de map `/public`.
|
||||
:::
|
||||
|
||||
## Probleemoplossing
|
||||
|
||||
### Build-timeout
|
||||
|
||||
Als builds een timeout geven, probeer dan:
|
||||
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 bij vernieuwen
|
||||
|
||||
Voeg een `vercel.json` toe voor SPA-routing:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
```
|
||||
|
||||
### Word/ODT/Excel naar PDF werkt niet
|
||||
|
||||
LibreOffice WASM-conversies vereisen `SharedArrayBuffer`, waarvoor deze responsheaders op alle pagina's nodig zijn:
|
||||
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
|
||||
De vooraf gecomprimeerde bestanden `.wasm.gz` en `.data.gz` hebben ook `Content-Encoding: gzip` nodig, zodat de browser ze decomprimeert.
|
||||
|
||||
Voeg deze toe aan je `vercel.json`:
|
||||
|
||||
```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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Controleer dit door de DevTools-console te openen en `console.log(window.crossOriginIsolated)` uit te voeren: het moet `true` teruggeven.
|
||||
Reference in new issue
Block a user