mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-11 01:14:35 +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 @@
|
||||
# Installer med Apache
|
||||
|
||||
Host BentoPDF med Apache HTTP Server.
|
||||
|
||||
## Forudsætninger
|
||||
|
||||
- Apache 2.4+
|
||||
- mod_rewrite aktiveret
|
||||
- SSL-certifikat (anbefales)
|
||||
|
||||
## Trin 1: Byg projektet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Hvis du vil tilpasse branding, skal du sætte miljøvariabler før build:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Trin 2: Kopiér filer
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Trin 3: Apache-konfiguration
|
||||
|
||||
Opret `/etc/apache2/sites-available/bentopdf.conf`:
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName your-domain.com
|
||||
Redirect permanent / https://your-domain.com/
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName your-domain.com
|
||||
DocumentRoot /var/www/bentopdf
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
|
||||
|
||||
<Directory /var/www/bentopdf>
|
||||
Options -Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
# WASM MIME type
|
||||
AddType application/wasm .wasm
|
||||
|
||||
# Prevent double-compression of pre-compressed files
|
||||
SetEnvIfNoCase Request_URI "\.gz$" no-gzip
|
||||
|
||||
# Compression
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json application/wasm
|
||||
</IfModule>
|
||||
|
||||
# Cache headers
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType application/javascript "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 year"
|
||||
ExpiresByType application/wasm "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
# Required headers for SharedArrayBuffer (LibreOffice WASM)
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Resource-Policy "cross-origin"
|
||||
|
||||
# Security headers
|
||||
Header always set X-Frame-Options "SAMEORIGIN"
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
|
||||
# Pre-compressed LibreOffice WASM files
|
||||
<FilesMatch "soffice\.wasm\.gz$">
|
||||
ForceType application/wasm
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
<FilesMatch "soffice\.data\.gz$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## Trin 4: .htaccess til routing
|
||||
|
||||
Opret `/var/www/bentopdf/.htaccess`:
|
||||
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# Existing files/dirs - serve directly
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# ============================================
|
||||
# LANGUAGE ROUTES
|
||||
# ============================================
|
||||
# Supported languages: de, es, zh, zh-TW, vi, it, id, tr, fr, pt
|
||||
# English has no prefix - served from root
|
||||
|
||||
# English prefix redirects to root
|
||||
RewriteRule ^en/?$ / [R=301,L]
|
||||
RewriteRule ^en/(.+)$ /$1 [R=301,L]
|
||||
|
||||
# Language prefix root (e.g., /de/ -> /de/index.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /$1/index.html [L]
|
||||
|
||||
# Language prefix with path (e.g., /de/merge-pdf -> /de/merge-pdf.html)
|
||||
RewriteCond %{DOCUMENT_ROOT}/$1/$2.html -f
|
||||
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /$1/$2.html [L]
|
||||
|
||||
# ============================================
|
||||
# ADD .HTML EXTENSION (ROOT LEVEL)
|
||||
# ============================================
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^([^/]+)$ $1.html [L]
|
||||
|
||||
ErrorDocument 404 /404.html
|
||||
```
|
||||
|
||||
## Trin 5: Aktivér de nødvendige moduler
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
sudo a2enmod ssl
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod expires
|
||||
sudo a2enmod deflate
|
||||
```
|
||||
|
||||
## Trin 6: Aktivér sitet
|
||||
|
||||
```bash
|
||||
sudo a2ensite bentopdf.conf
|
||||
sudo apache2ctl configtest
|
||||
sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
## Installation i en undermappe
|
||||
|
||||
For at hoste på `/pdf/`:
|
||||
|
||||
1. Byg med base-URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
2. Opdater `.htaccess`:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### WASM 404-fejl
|
||||
|
||||
Sørg for, at MIME-typen er konfigureret:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Underskriv PDF eller Formularudfylder viser en tom viewer (`.mjs` MIME-fejl)
|
||||
|
||||
Hvis browserkonsollen viser:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…og den mislykkede forespørgsel ender på `.mjs`, mangler din Apache-konfiguration tilknytningen `.mjs → application/javascript`. Den medfølgende PDF-viewer leveres med ES-modulfiler med filtypen `.mjs`, og Apaches standard-`mime.types` indeholder dem ikke som standard.
|
||||
|
||||
Tilføj til din VirtualHost eller `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/javascript .mjs
|
||||
```
|
||||
|
||||
Genindlæs derefter Apache:
|
||||
|
||||
```bash
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
Sådan bekræfter du: find den mislykkede `.mjs`-forespørgsel under fanen Network i browserens DevTools, og tjek svarheaderen `Content-Type`. Den skal nu være `application/javascript`, ikke `application/octet-stream`.
|
||||
|
||||
### Rewrite virker ikke
|
||||
|
||||
Tjek, at mod_rewrite er aktiveret:
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
### Word/ODT/Excel til PDF virker ikke
|
||||
|
||||
LibreOffice WASM kræver `SharedArrayBuffer`, som kræver disse headers:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
Det kræver også en sikker kontekst. `http://localhost` virker til lokal test, men `http://192.168.x.x` eller andre LAN-IP'er kræver normalt HTTPS. Hvis headerne er til stede, men `window.crossOriginIsolated` stadig er `false`, så tjek, om siden åbnes over almindelig HTTP på en origin, der ikke er loopback.
|
||||
|
||||
De forkomprimerede `.wasm.gz`- og `.data.gz`-filer kræver også korrekt `Content-Encoding`:
|
||||
|
||||
```apache
|
||||
<FilesMatch "soffice\.wasm\.gz$">
|
||||
ForceType application/wasm
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
<FilesMatch "soffice\.data\.gz$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Encoding "gzip"
|
||||
</FilesMatch>
|
||||
```
|
||||
|
||||
Sørg for, at `mod_headers` er aktiveret: `sudo a2enmod headers`
|
||||
|
||||
### Tilladelse nægtet
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
sudo chmod -R 755 /var/www/bentopdf
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
# Installer på AWS S3 + CloudFront
|
||||
|
||||
Host BentoPDF på AWS for maksimal kontrol og skalerbarhed.
|
||||
|
||||
## Arkitektur
|
||||
|
||||
```
|
||||
User → CloudFront (CDN) → S3 (Static Files)
|
||||
```
|
||||
|
||||
## Trin 1: Opret 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
|
||||
```
|
||||
|
||||
## Trin 2: Byg og upload
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Trin 3: Opret CloudFront-distribution
|
||||
|
||||
```bash
|
||||
aws cloudfront create-distribution \
|
||||
--origin-domain-name your-bentopdf-bucket.s3.amazonaws.com \
|
||||
--default-root-object index.html
|
||||
```
|
||||
|
||||
Eller brug AWS Console:
|
||||
|
||||
1. Gå til CloudFront → Create distribution
|
||||
2. Origin domain: Vælg din S3-bucket
|
||||
3. Aktivér "Origin Access Control"
|
||||
4. Default root object: `index.html`
|
||||
5. Opret distributionen
|
||||
|
||||
## Trin 3b: Response Headers Policy (påkrævet til LibreOffice WASM)
|
||||
|
||||
LibreOffice-baserede konverteringer (Word, Excel, PowerPoint til PDF) kræver `SharedArrayBuffer`, som kræver bestemte svarheadere. Opret en CloudFront Response Headers Policy:
|
||||
|
||||
1. Gå til CloudFront → Policies → Response headers
|
||||
2. Opret en brugerdefineret policy med disse headere:
|
||||
|
||||
| Header | Værdi |
|
||||
| ------------------------------ | -------------- |
|
||||
| `Cross-Origin-Embedder-Policy` | `require-corp` |
|
||||
| `Cross-Origin-Opener-Policy` | `same-origin` |
|
||||
| `Cross-Origin-Resource-Policy` | `cross-origin` |
|
||||
|
||||
3. Tilknyt policyen til din distributions standard-cache behavior
|
||||
|
||||
Eller via 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}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Trin 3c: S3-metadata til forkomprimerede WASM-filer
|
||||
|
||||
LibreOffice WASM-filerne er forkomprimerede (`.wasm.gz`, `.data.gz`). Sæt den korrekte Content-Type og Content-Encoding, så browsere dekomprimerer dem:
|
||||
|
||||
```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 Vigtigt
|
||||
Uden response headers policy er `SharedArrayBuffer` ikke tilgængelig, og LibreOffice WASM-konverteringer hænger ved ca. 55 %. Uden den korrekte Content-Encoding på `.gz`-filerne modtager browseren rå gzip-bytes, og WASM-kompilering mislykkes.
|
||||
:::
|
||||
|
||||
## Trin 4: S3 bucket policy
|
||||
|
||||
Giv CloudFront adgang til bucketten:
|
||||
|
||||
```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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Trin 5: Brugerdefinerede fejlsider
|
||||
|
||||
Konfigurer 404 til at returnere `index.html` til SPA-routing:
|
||||
|
||||
1. CloudFront → Error pages
|
||||
2. Opret et brugerdefineret fejlsvar:
|
||||
- HTTP error code: 404
|
||||
- Response page path: `/index.html`
|
||||
- HTTP response code: 200
|
||||
|
||||
## Omkostningsestimat
|
||||
|
||||
| Ressource | Estimeret pris |
|
||||
| -------------------------- | -------------- |
|
||||
| S3 Storage (~500MB) | ~$0.01/måned |
|
||||
| CloudFront (1TB transfer) | ~$85/måned |
|
||||
| CloudFront (10GB transfer) | ~$0.85/måned |
|
||||
|
||||
::: tip
|
||||
Brug S3 Intelligent Tiering til at spare penge på filer, der sjældent tilgås.
|
||||
:::
|
||||
|
||||
## Automatisering med 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 @@
|
||||
# Installer på Cloudflare Pages
|
||||
|
||||
[Cloudflare Pages](https://pages.cloudflare.com) tilbyder hurtig, global hosting af statiske sites med ubegrænset båndbredde.
|
||||
|
||||
## Hurtig installation
|
||||
|
||||
1. Gå til [Cloudflare Pages](https://dash.cloudflare.com/?to=/:account/pages)
|
||||
2. Klik på "Create a project"
|
||||
3. Forbind dit GitHub-repository
|
||||
|
||||
## Build-konfiguration
|
||||
|
||||
| Indstilling | Værdi |
|
||||
| ---------------------- | --------------- |
|
||||
| Framework preset | None |
|
||||
| Build command | `npm run build` |
|
||||
| Build output directory | `dist` |
|
||||
| Root directory | `/` |
|
||||
|
||||
## Miljøvariabler
|
||||
|
||||
Tilføj disse under Settings → Environment variables:
|
||||
|
||||
| Variabel | Værdi |
|
||||
| ----------------------- | ---------------------------------------------------- |
|
||||
| `NODE_VERSION` | `18` |
|
||||
| `SIMPLE_MODE` | `false` (valgfrit) |
|
||||
| `VITE_BRAND_NAME` | Brugerdefineret brandnavn (valgfrit) |
|
||||
| `VITE_BRAND_LOGO` | Logosti relativt til `public/` (valgfrit) |
|
||||
| `VITE_FOOTER_TEXT` | Brugerdefineret footer-/copyrighttekst (valgfrit) |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standardsprog i brugerfladen, f.eks. `fr` (valgfrit) |
|
||||
|
||||
## Konfigurationsfil
|
||||
|
||||
Opret `_headers` i din `public`-mappe:
|
||||
|
||||
```
|
||||
# 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 Vigtigt
|
||||
Headerne `Cross-Origin-Embedder-Policy` og `Cross-Origin-Opener-Policy` er påkrævet til konvertering af Word/ODT/Excel/PowerPoint til PDF. Uden dem er `SharedArrayBuffer` ikke tilgængelig, og LibreOffice WASM-motoren kan ikke starte.
|
||||
:::
|
||||
|
||||
Opret `_redirects` til SPA-routing:
|
||||
|
||||
```
|
||||
/* /index.html 200
|
||||
```
|
||||
|
||||
## Eget domæne
|
||||
|
||||
1. Gå til dit Pages-projekt
|
||||
2. Klik på "Custom domains"
|
||||
3. Tilføj dit domæne
|
||||
4. Cloudflare konfigurerer automatisk DNS, hvis domænet ligger hos Cloudflare
|
||||
|
||||
## Fordele
|
||||
|
||||
- **Gratis, ubegrænset båndbredde**
|
||||
- **Globalt CDN** med 300+ edge-lokationer
|
||||
- **Automatisk HTTPS**
|
||||
- **Preview-installationer** til pull requests
|
||||
- **Hurtige builds**
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### Upload af store filer
|
||||
|
||||
Cloudflare Pages understøtter filer op til 25 MB. WASM-moduler bør være fine, men hvis du rammer grænser, kan du overveje:
|
||||
|
||||
```bash
|
||||
# Split large files during build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Størrelsesgrænser for Workers
|
||||
|
||||
Hvis du bruger Cloudflare Workers til avanceret routing, skal du være opmærksom på grænsen på 1 MB for gratis planer.
|
||||
|
||||
## CORS-proxy-worker (til digitale signaturer)
|
||||
|
||||
Værktøjet til digital signatur kræver en CORS-proxy for at hente certifikatkæder. Installer den medfølgende worker:
|
||||
|
||||
```bash
|
||||
cd cloudflare
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Sikkerhedsfunktioner
|
||||
|
||||
| Funktion | Beskrivelse |
|
||||
| ------------------------------ | ----------------------------------- |
|
||||
| **URL-begrænsninger** | Kun certifikat-URL'er tilladt |
|
||||
| **Filstørrelsesgrænse** | Maks. 10MB pr. forespørgsel |
|
||||
| **Rate limiting** | 60 forespørgsler/IP/min (kræver KV) |
|
||||
| **Blokering af private IP'er** | Blokerer localhost og interne IP'er |
|
||||
|
||||
### Aktivér rate limiting
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Byg med proxy-URL
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
Eller med 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 .
|
||||
```
|
||||
|
||||
> **Bemærk:** Se [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required) for opsætning af HMAC-signatur.
|
||||
@@ -0,0 +1,180 @@
|
||||
# CORS-proxy til hentning af certifikater
|
||||
|
||||
Værktøjet til digital signatur bruger en CORS-proxy til at hente udstedercertifikater fra eksterne certifikatudstedere (CA'er). Det er nødvendigt, fordi mange CA-servere ikke inkluderer CORS-headere i deres svar, hvilket forhindrer direkte hentning fra browseren.
|
||||
|
||||
Derudover udleverer mange CA-servere certifikater over almindelig HTTP. Når din BentoPDF-instans hostes over HTTPS, blokerer browsere disse HTTP-forespørgsler (mixed content-politik). CORS-proxyen løser begge problemer ved at route forespørgsler gennem et HTTPS-endpoint med de rette headere.
|
||||
|
||||
## Sådan fungerer det
|
||||
|
||||
Når du signerer en PDF med et certifikat:
|
||||
|
||||
1. Biblioteket `zgapdfsigner` forsøger at bygge en komplet certifikatkæde
|
||||
2. Det henter udstedercertifikater fra URL'er indlejret i dit certifikats AIA-udvidelse (Authority Information Access)
|
||||
3. Disse forespørgsler routes gennem en CORS-proxy, der tilføjer de nødvendige `Access-Control-Allow-Origin`-headere
|
||||
4. Proxyen returnerer certifikatdata til browseren
|
||||
|
||||
## Selvhosting af CORS-proxyen
|
||||
|
||||
Hvis du selvhoster BentoPDF, skal du installere din egen CORS-proxy, for at digitale signaturer virker med certifikater, der kræver hentning af kæden.
|
||||
|
||||
### Mulighed 1: Cloudflare Workers (anbefales)
|
||||
|
||||
1. **Installer Wrangler CLI**:
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
```
|
||||
|
||||
2. **Log ind på Cloudflare**:
|
||||
|
||||
```bash
|
||||
wrangler login
|
||||
```
|
||||
|
||||
3. **Klon BentoPDF og opdater tilladte origins**:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf/cloudflare
|
||||
```
|
||||
|
||||
Åbn `cors-proxy-worker.js` og ret arrayet `ALLOWED_ORIGINS` til dit domæne:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = [
|
||||
'https://your-domain.com',
|
||||
'https://www.your-domain.com',
|
||||
];
|
||||
```
|
||||
|
||||
::: warning Vigtigt
|
||||
Uden denne ændring afviser proxyen alle forespørgsler fra dit site med fejlen **403 Forbidden**. Standarden tillader kun forespørgsler fra `bentopdf.com`.
|
||||
:::
|
||||
|
||||
4. **Installer proxyen**:
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Notér din worker-URL (f.eks. `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
|
||||
|
||||
5. **Genbyg BentoPDF med proxy-URL'en**:
|
||||
|
||||
Hvis du bruger 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 .
|
||||
```
|
||||
|
||||
Hvis du bygger fra kildekode:
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
### Mulighed 2: Egen backend-proxy
|
||||
|
||||
Du kan også oprette dit eget proxy-endpoint. Kravene er:
|
||||
|
||||
1. Acceptér GET-forespørgsler med en `url`-forespørgselsparameter
|
||||
2. Hent URL'en fra din server (ingen CORS-begrænsninger på serversiden)
|
||||
3. Returnér svaret med disse headere:
|
||||
- `Access-Control-Allow-Origin: https://your-domain.com`
|
||||
- `Access-Control-Allow-Methods: GET, OPTIONS`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
|
||||
Eksempel på en implementering i 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' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Sikkerhedsovervejelser
|
||||
|
||||
Den medfølgende Cloudflare Worker har flere sikkerhedsforanstaltninger:
|
||||
|
||||
| Funktion | Beskrivelse |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------ |
|
||||
| **Origin-validering** | Tillader kun forespørgsler fra domæner på listen `ALLOWED_ORIGINS` |
|
||||
| **URL-begrænsninger** | Tillader kun certifikat-URL'er (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`) |
|
||||
| **Blokering af private IP'er** | Blokerer private IPv4/IPv6-områder, link-local, loopback, decimale IP'er og cloud-metadata |
|
||||
| **Content-Type-sikkerhed** | Returnerer kun sikre MIME-typer for certifikater og blokerer content-type-injektion fra upstream |
|
||||
| **Filstørrelsesgrænse** | Streamer svaret med en grænse på 10MB og afbryder midt i overførslen, hvis den overskrides |
|
||||
| **Rate limiting** | 60 forespørgsler pr. minut pr. IP eller IPv6 /64 (kræver KV, fejler åbent ved KV-fejl) |
|
||||
| **HMAC-signaturer** | Valgfri signering på klientsiden (afskrækker tilfældigt misbrug) |
|
||||
|
||||
### DNS rebinding (selvhosting uden for Cloudflare)
|
||||
|
||||
Workeren afviser enhver URL, der resolver til en privat eller intern IP. Den kan ikke bruge en fast tilladelsesliste over værter, fordi URL'er i certifikatkæder peger på den CA, der har udstedt certifikatet (FNMT, virksomheders CA'er og så videre), så destinationerne kendes ikke på forhånd. Det efterlader et lille rebinding-vindue: en person, der driver DNS for sit eget domæne, kan give sikkerhedstjekket en offentlig IP og den egentlige forespørgsel en privat lidt senere.
|
||||
|
||||
På Cloudflare kan det ikke udnyttes: Workers kan ikke nå private IP'er eller metadata-endpointet `169.254.169.254`, så en rebind rammer ingenting. Giv bare ikke workeren en vej ind: tilknyt ikke en Tunnel, Service eller binding til et privat netværk (den behøver kun `RATE_LIMIT_KV`).
|
||||
|
||||
Hvis du kører den uden for Cloudflare (workerd, Node og lignende) i en VPC, _kan_ runtime'en nå interne IP'er. Sæt en egress-firewall foran, og bloker de private områder: `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` og deres IPv6-ækvivalenter. Det kan applikationskode ikke håndhæve, når du er uden for Cloudflare.
|
||||
|
||||
## Deaktivering af proxyen
|
||||
|
||||
Hvis du ikke vil bruge en CORS-proxy, så sæt miljøvariablen til en tom streng:
|
||||
|
||||
```
|
||||
VITE_CORS_PROXY_URL=
|
||||
```
|
||||
|
||||
**Bemærk**: Uden proxyen mislykkes signering med certifikater, der kræver ekstern hentning af kæden (som FNMT eller nogle virksomheders CA'er), med fejlen "Failed to fetch".
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### "Signing error: TypeError: Failed to fetch"
|
||||
|
||||
Det betyder normalt enten:
|
||||
|
||||
1. **Ingen CORS-proxy konfigureret**: Sæt `VITE_CORS_PROXY_URL` og genbyg
|
||||
2. **Mixed content blokeret**: Dit site bruger HTTPS, men certifikatets udsteder-URL er HTTP. CORS-proxyen løser dette.
|
||||
3. **CORS-proxyen afviser din origin**: Tjek, at dit domæne står i arrayet `ALLOWED_ORIGINS` i `cors-proxy-worker.js`
|
||||
|
||||
### "403 Forbidden" fra proxyen
|
||||
|
||||
Dit domæne står ikke på listen `ALLOWED_ORIGINS`. Rediger `cors-proxy-worker.js`:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = ['https://your-domain.com'];
|
||||
```
|
||||
|
||||
Installer derefter igen: `npx wrangler deploy`
|
||||
|
||||
### Test af proxyen
|
||||
|
||||
```bash
|
||||
curl -H "Origin: https://your-domain.com" \
|
||||
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"
|
||||
```
|
||||
|
||||
### Certifikater, der virker uden proxy
|
||||
|
||||
Nogle certifikater indeholder hele kæden i P12/PFX-filen og kræver ikke ekstern hentning:
|
||||
|
||||
- Selvsignerede certifikater
|
||||
- Nogle kommercielle CA'er, der medsender mellemliggende certifikater
|
||||
- Certifikater, du selv har samlet med hele kæden
|
||||
@@ -0,0 +1,622 @@
|
||||
# Installer med Docker / Podman
|
||||
|
||||
Den nemmeste måde at selvhoste BentoPDF i et produktionsmiljø.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Påkrævede headere til konvertering af Office-filer**
|
||||
>
|
||||
> LibreOffice-baserede værktøjer (konvertering af Word, Excel og PowerPoint) kræver disse HTTP-headere for at understøtte `SharedArrayBuffer`:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Siden skal også serveres fra en sikker kontekst. `http://localhost` virker til lokal test, men `http://192.168.x.x` eller andre LAN-IP'er opfylder normalt ikke kravet, så Office-konvertering over almindelig HTTP mislykkes, selv om headerne er til stede.
|
||||
>
|
||||
> De officielle containerbilleder indeholder disse headere. Hvis du bruger en reverse proxy (Traefik, Caddy osv.), så sørg for, at headerne bevares eller tilføjes, og brug HTTPS ved adgang uden for loopback.
|
||||
|
||||
> [!TIP]
|
||||
> **Podman-brugere:** Alle `docker`-kommandoer virker med Podman, hvis du erstatter `docker` med `podman` og `docker-compose` med `podman-compose`.
|
||||
|
||||
## Hurtig start
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF leveres i to builds:
|
||||
>
|
||||
> - **Self-Hosted-build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Alle PDF-værktøjer, **uden** markedsføringsindhold (ingen hero, FAQ, udtalelser, footer). Brug den til interne installationer i teams og organisationer. Det er **ikke** en "lite"-version med færre funktioner.
|
||||
> - **Commercial-build**: `ghcr.io/alam00000/bentopdf:latest`. Det fulde markedsføringssite, som bruges af bentopdf.com selv og af indehavere af kommercielle licenser med offentligt tilgængelige installationer.
|
||||
>
|
||||
> Er du i tvivl: hent Self-Hosted-builden.
|
||||
|
||||
```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
|
||||
|
||||
Opret `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
container_name: bentopdf
|
||||
ports:
|
||||
- '3000:8080'
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:8080']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
Kør:
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Byg dit eget 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;"]
|
||||
```
|
||||
|
||||
Byg og kør:
|
||||
|
||||
```bash
|
||||
docker build -t bentopdf:custom .
|
||||
docker run -d -p 3000:8080 bentopdf:custom
|
||||
```
|
||||
|
||||
## Miljøvariabler
|
||||
|
||||
| Variabel | Beskrivelse | Standard |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Byg Self-Hosted-varianten (skjuler bentopdf.com's markedsføringsindhold: hero, FAQ, udtalelser, footer). Alle PDF-værktøjer bevares. | `false` |
|
||||
| `BASE_URL` | Installer i en undermappe | `/` |
|
||||
| `VITE_WASM_PYMUPDF_URL` | URL til PyMuPDF WASM-modul | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/` |
|
||||
| `VITE_WASM_GS_URL` | URL til Ghostscript WASM-modul | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/` |
|
||||
| `VITE_WASM_CPDF_URL` | URL til CoherentPDF WASM-modul | `https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/` |
|
||||
| `VITE_TESSERACT_WORKER_URL` | URL til OCR-workerscript | _(tom; brug Tesseract.js' standard-CDN)_ |
|
||||
| `VITE_TESSERACT_CORE_URL` | Mappe til OCR-kerne-runtime | _(tom; brug Tesseract.js' standard-CDN)_ |
|
||||
| `VITE_TESSERACT_LANG_URL` | Mappe til OCR-traineddata | _(tom; brug Tesseract.js' standard-CDN)_ |
|
||||
| `VITE_TESSERACT_AVAILABLE_LANGUAGES` | Kommaseparerede OCR-sprog, der vises i brugerfladen | _(tom; vis hele kataloget)_ |
|
||||
| `VITE_OCR_FONT_BASE_URL` | Mappe til OCR-tekstlagets skrifttyper | _(tom; brug eksterne Noto-skrift-URL'er)_ |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standard UI language | `en` |
|
||||
| `VITE_BRAND_NAME` | Brugerdefineret brandnavn | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logosti relativt til `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Brugerdefineret footer-/copyrighttekst | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | Kommaseparerede værktøjs-ID'er, der skal skjules | _(tom; alle værktøjer aktiveret)_ |
|
||||
| `DISABLE_GITHUB_STARS` | Spring hentningen af GitHub-stjerneantal over ved sideindlæsning, og fjern `api.github.com` fra CSP. Simple Mode-builds springer den altid over. | `false` |
|
||||
| `SITE_URL` | Offentlig URL, der skrives i canonical-, hreflang- og sitemap-links. Lad den stå på standardværdien, medmindre du vil have søgemaskiner til at behandle din installation som et eget site. | `https://www.bentopdf.com` |
|
||||
| `ALLOW_INDEXING` | Med en brugerdefineret `SITE_URL` markeres hver side `noindex`, så din kopi ikke konkurrerer med bentopdf.com i søgeresultaterne. Sæt til `true` for at lade søgemaskiner indeksere den. | `false` |
|
||||
|
||||
WASM-modul-URL'erne er forudkonfigureret med CDN-standarder, så alle avancerede funktioner virker fra start. Overskriv dem til air-gapped eller selvhostede installationer.
|
||||
|
||||
### Søgemaskiner
|
||||
|
||||
Som standard peger hver side søgemaskiner hen på bentopdf.com som originalen, så din installation ikke vises som dublet. Du behøver ikke ændre noget for en privat eller intern instans.
|
||||
|
||||
- **Hold din instans helt ude af søgeresultaterne:** kør containeren med `-e ROBOTS_NOINDEX=true`. Det kræver kun en genstart, ikke en ny build.
|
||||
- **Gør din instans til sit eget site i søgeresultaterne:** byg med `--build-arg SITE_URL=https://your-domain.example --build-arg ALLOW_INDEXING=true`. Uden `ALLOW_INDEXING=true` markerer en brugerdefineret `SITE_URL` hver side `noindex`.
|
||||
|
||||
### Content-Security-Policy
|
||||
|
||||
Nginx-imaget leveres med en håndhævende `Content-Security-Policy`-header. CSP'ens direktiver `script-src`, `connect-src` og `font-src` **genereres ved build** ud fra `VITE_*`-URL-variablerne ovenfor, og alle værter, du angiver via `--build-arg`, føjes automatisk til politikken.
|
||||
|
||||
Det betyder:
|
||||
|
||||
- Hvis du overskriver `VITE_CORS_PROXY_URL` eller `VITE_WASM_*_URL` ved build, tillader CSP automatisk disse origins, så der kræves ingen ekstra konfiguration.
|
||||
- For URL'er på `cdn.jsdelivr.net` tillader `script-src` kun den præcise versionerede pakkemappe, for eksempel `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/`, ikke hele CDN'et. jsDelivr leverer enhver offentlig npm-pakke eller GitHub-repository, så hvis hele værten blev tilladt, kunne hvilken som helst af dem køre på din instans. URL'er på alle andre værter tillades pr. origin.
|
||||
- Hvis du konfigurerer brugerdefinerede WASM-URL'er _ved kørsel_ via siden Avancerede indstillinger i appen, står disse origins **ikke** i CSP, og browseren blokerer hentning fra dem. Det gælder også en anden version af samme pakke på `cdn.jsdelivr.net`, fordi kun den version, der blev sat ved build, er tilladt. Konfiguration ved kørsel er beregnet til eksperimenter. Til permanente brugerdefinerede URL'er skal du sætte det tilsvarende `VITE_*`-build-arg.
|
||||
- Air-gapped installationer, der overskriver alle tre `VITE_WASM_*_URL`-værdier, får også disse `cdn.jsdelivr.net`-pakkemapper fjernet fra CSP (hver standardværdi erstattes, ikke tilføjes). Tesseract OCR-standarderne på `cdn.jsdelivr.net` forbliver i politikken, indtil du også sætter de tre `VITE_TESSERACT_*`-værdier. Tilsvarende erstatter `VITE_CORS_PROXY_URL` den offentlige standard `bentopdf-cors-proxy.bentopdf.workers.dev`.
|
||||
|
||||
CSP indeholder `'unsafe-eval'` i `script-src`, fordi LibreOffice WASM-runtime (brugt af værktøjerne til konvertering af Word/Excel/PowerPoint) og billedbiblioteket wasm-vips (brugt til PDF til TIFF) kompilerer intern dispatch-kode via `new Function()`. Hvis den fjernes, holder de værktøjer op med at virke. Hvis du bygger i `SIMPLE_MODE` (uden LibreOffice) og ikke har brug for PDF til TIFF, kan du manuelt redigere den genererede `security-headers.conf` og fjerne `'unsafe-eval'` for en strengere politik.
|
||||
|
||||
For OCR skal du lade `VITE_TESSERACT_*`-variablerne stå tomme for at bruge standardressourcerne online, eller sætte alle tre sammen til selvhostet/offline OCR. Delvise OCR-overskrivninger afvises, fordi worker, kerne-runtime og traineddata-mappe skal passe sammen. For fuldt offline søgbar PDF-output skal du også sætte `VITE_OCR_FONT_BASE_URL`, så OCR-tekstlagets skrifttyper indlæses fra din interne server i stedet for de offentlige Noto-skrift-URL'er.
|
||||
|
||||
`VITE_DEFAULT_LANGUAGE` angiver brugerfladens sprog for førstegangsbesøgende. Understøttede værdier: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Brugere kan stadig skifte sprog, det ændrer kun standarden.
|
||||
|
||||
Eksempel:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Brugerdefineret branding
|
||||
|
||||
Erstat BentoPDF's standardlogo, navn og footertekst med dine egne. Læg din logofil i mappen `public/` (eller brug et eksisterende billede), og angiv derefter branding-variablerne ved build:
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg VITE_BRAND_NAME="AcmePDF" \
|
||||
--build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
--build-arg VITE_FOOTER_TEXT="© 2026 Acme Corp. Internal use only." \
|
||||
-t acmepdf .
|
||||
```
|
||||
|
||||
Branding virker i både fuld tilstand og Simple Mode og kan kombineres med alle andre build-indstillinger.
|
||||
|
||||
### Deaktivering af bestemte værktøjer
|
||||
|
||||
Skjul værktøjer i brugerfladen af hensyn til compliance eller sikkerhed. Deaktiverede værktøjer fjernes fra forsiden, søgeresultater, tastaturgenveje og workflow-builderen. Direkte adgang via URL viser en side om, at værktøjet ikke er tilgængeligt.
|
||||
|
||||
Værktøjs-ID'er er sidens URL uden `.html`. Hvis værktøjet for eksempel ligger på `bentopdf.com/edit-pdf.html`, er ID'et `edit-pdf`.
|
||||
|
||||
#### Find værktøjs-ID'er
|
||||
|
||||
Den nemmeste måde: åbn et værktøj i BentoPDF og se på URL'en. Den sidste del af stien (uden `.html`) er værktøjs-ID'et.
|
||||
|
||||
<details>
|
||||
<summary>Komplet liste over værktøjs-ID'er</summary>
|
||||
|
||||
**Rediger og annotér:** `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`
|
||||
|
||||
**Konverter til PDF:** `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`
|
||||
|
||||
**Konverter fra PDF:** `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`
|
||||
|
||||
**Organiser og administrer:** `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`
|
||||
|
||||
**Optimer og reparer:** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Sikkerhed:** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Mulighed 1: Ved build (Docker build-arg)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
Det bager listen over deaktiverede værktøjer ind i JavaScript-bundlen. Kræver en ny build at ændre.
|
||||
|
||||
#### Mulighed 2: Ved kørsel (config.json)
|
||||
|
||||
Mount en `config.json`-fil i den serverede mappe, uden behov for ny 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
|
||||
```
|
||||
|
||||
Eller med 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
|
||||
```
|
||||
|
||||
Begge metoder kan kombineres, og listerne flettes. Hvis et værktøj står i en af dem, er det deaktiveret.
|
||||
|
||||
#### Deaktivering af editorfunktioner
|
||||
|
||||
Du kan også deaktivere bestemte funktioner i PDF-editoren (f.eks. sortlægning, annotationer, formularer) uden at deaktivere hele editorværktøjet. Tilføj `editorDisabledCategories` til din `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"editorDisabledCategories": ["redaction", "annotation-stamp"]
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Komplet liste over editorkategorier</summary>
|
||||
|
||||
**Zoom:** `zoom`, `zoom-in`, `zoom-out`, `zoom-fit-page`, `zoom-fit-width`, `zoom-marquee`, `zoom-level`
|
||||
|
||||
**Annotation:** `annotation`, `annotation-markup`, `annotation-highlight`, `annotation-underline`, `annotation-strikeout`, `annotation-squiggly`, `annotation-ink`, `annotation-text`, `annotation-stamp`
|
||||
|
||||
**Former:** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
|
||||
|
||||
**Formular:** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
|
||||
|
||||
**Sortlægning:** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
|
||||
|
||||
**Dokument:** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
|
||||
|
||||
**Side:** `page`, `spread`, `rotate`, `scroll`, `navigation`
|
||||
|
||||
**Panel:** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
|
||||
|
||||
**Værktøjer:** `tools`, `pan`, `pointer`, `capture`
|
||||
|
||||
**Markering:** `selection`, `selection-copy`
|
||||
|
||||
**Historik:** `history`, `history-undo`, `history-redo`
|
||||
|
||||
</details>
|
||||
|
||||
Kategorierne er hierarkiske: Deaktiverer du en overordnet kategori (f.eks. `annotation`), deaktiveres alle dens underkategorier.
|
||||
|
||||
### Deaktivering af GitHub-stjernetælleren
|
||||
|
||||
Navigationslinjen viser et live antal GitHub-stjerner, som hentes fra `api.github.com` ved sideindlæsning. Det er den eneste eksterne forespørgsel, der ikke tjener en PDF-funktion. Deaktivér den ved build for at fjerne hentekoden fra bundlen og fjerne `api.github.com` fra Content-Security-Policy:
|
||||
|
||||
```bash
|
||||
docker build --build-arg DISABLE_GITHUB_STARS=true -t bentopdf .
|
||||
```
|
||||
|
||||
Simple Mode-builds (`bentopdf-simple`) springer altid stjernetælleren over. Flaget er kun nødvendigt på Commercial-builden eller egne fulde builds.
|
||||
|
||||
### Brugerdefinerede WASM-URL'er (air-gapped / selvhostet)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> WASM-URL'er bages ind i JavaScript **ved build**. WASM-filerne downloades af **brugerens browser** ved kørsel, og Docker downloader dem ikke under build. I air-gapped netværk skal du hoste WASM-filerne på en intern server, som browsere kan nå.
|
||||
|
||||
**Fuld air-gapped arbejdsgang:**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Brug koderne udskrevet af `bash scripts/prepare-airgap.sh --list-ocr-languages`, eller søg på navn med `bash scripts/prepare-airgap.sh --search-ocr-language <term>`, til `--ocr-languages`. Når du bygger med en begrænset OCR-delmængde, skal du give de samme koder til `VITE_TESSERACT_AVAILABLE_LANGUAGES`, så appen kun viser medfølgende sprog. For fuld offline OCR-output skal du også hoste den medfølgende mappe `ocr-fonts/` og pege `VITE_OCR_FONT_BASE_URL` på den.
|
||||
|
||||
Sæt en variabel til en tom streng for at deaktivere det modul (brugere skal så konfigurere manuelt via Avancerede indstillinger).
|
||||
|
||||
## Brugerdefineret port
|
||||
|
||||
Som standard lytter BentoPDF på port `8080` inde i containeren. For at ændre det skal du sætte miljøvariablen `PORT` ved kørsel:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
| Variable | Beskrivelse | Standard |
|
||||
| -------- | ------------------------------ | -------- |
|
||||
| `PORT` | Nginx' lytteport i containeren | `8080` |
|
||||
|
||||
Det virker med både standard- og nonroot-Dockerfilerne.
|
||||
|
||||
## Brugerdefineret bruger-ID (PUID/PGID)
|
||||
|
||||
Til miljøer, der kræver kørsel som en bestemt bruger uden root (NAS-enheder, Kubernetes med sikkerhedskontekster, organisationens politikker), tilbyder BentoPDF en separat Dockerfile med PUID/PGID-understøttelse i LSIO-stil.
|
||||
|
||||
### Byg og kør
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Miljøvariabler
|
||||
|
||||
| Variabel | Beskrivelse | Standard |
|
||||
| -------------- | --------------------- | -------- |
|
||||
| `PUID` | Bruger-ID at køre som | `1000` |
|
||||
| `PGID` | Gruppe-ID at køre som | `1000` |
|
||||
| `PORT` | Nginx' lytteport | `8080` |
|
||||
| `DISABLE_IPV6` | Deaktivér IPv6-lytter | `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
|
||||
```
|
||||
|
||||
### Sådan fungerer det
|
||||
|
||||
Containeren starter som root, opretter en bruger med den angivne PUID/PGID, justerer ejerskab på alle skrivbare mapper og dropper derefter rettigheder med `su-exec`. Nginx-processen kører udelukkende som den bruger, du har angivet.
|
||||
|
||||
> [!NOTE]
|
||||
> Standard-`Dockerfile` bruger `nginx-unprivileged` (UID 101) og anbefales til de fleste installationer. Brug kun `Dockerfile.nonroot`, når du har brug for et bestemt UID/GID.
|
||||
|
||||
> [!WARNING]
|
||||
> PUID/PGID kan ikke være `0` (root). Entrypointet validerer input og afslutter med en fejl ved ugyldige værdier.
|
||||
|
||||
## Med 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
|
||||
```
|
||||
|
||||
## Med 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"
|
||||
}
|
||||
```
|
||||
|
||||
## Ressourcegrænser
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
```
|
||||
|
||||
## Podman Quadlet (systemd-integration)
|
||||
|
||||
[Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) giver dig mulighed for at køre Podman-containere som systemd-services. Det er ideelt til produktionsinstallationer på Linux-systemer.
|
||||
|
||||
### Grundlæggende Quadlet-opsætning
|
||||
|
||||
Opret en container-unitfil på `~/.config/containers/systemd/bentopdf.container` (bruger) eller `/etc/containers/systemd/bentopdf.container` (system):
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=BentoPDF - Privacy-first PDF toolkit
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
### Aktivér og start
|
||||
|
||||
```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]
|
||||
> Til installation for hele systemet skal du bruge `systemctl` uden flaget `--user` og placere filen i `/etc/containers/systemd/`.
|
||||
|
||||
### Quadlet med 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
|
||||
```
|
||||
|
||||
### Automatisk opdatering med Quadlet
|
||||
|
||||
Podman kan automatisk opdatere containere, når nye images er tilgængelige:
|
||||
|
||||
```bash
|
||||
# Enable auto-update timer
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
|
||||
# Check for updates manually
|
||||
podman auto-update
|
||||
|
||||
# Dry run (check without updating)
|
||||
podman auto-update --dry-run
|
||||
```
|
||||
|
||||
### Quadlet-netværkskonfiguration
|
||||
|
||||
Til brugerdefineret netværkskonfiguration skal du oprette en netværksfil `bentopdf.network`:
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Subnet=10.89.0.0/24
|
||||
Gateway=10.89.0.1
|
||||
```
|
||||
|
||||
Henvis derefter til den i din containerfil:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
Network=bentopdf.network
|
||||
```
|
||||
|
||||
## Opdatering
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Recreate container
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Installer på Hostinger
|
||||
|
||||
[Hostinger](https://hostinger.com) er en populær udbyder af shared hosting. Denne vejledning beskriver, hvordan du installerer BentoPDF på Hostingers shared hosting.
|
||||
|
||||
## Forudsætninger
|
||||
|
||||
- Hostinger-hostingplan med adgang til filhåndtering
|
||||
- Node.js installeret lokalt til build
|
||||
|
||||
## Trin 1: Byg projektet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
De byggede filer ligger i mappen `dist`.
|
||||
|
||||
## Trin 2: Upload til Hostinger
|
||||
|
||||
### Installation på rodomænet
|
||||
|
||||
1. Log ind på din Hostinger-konto
|
||||
2. Gå til **File Manager** → **public_html**
|
||||
3. **Slet** eventuelle eksisterende filer (tag backup, hvis nødvendigt)
|
||||
4. **Upload** hele indholdet af din lokale `dist`-mappe til `public_html`
|
||||
|
||||
### Installation i en undermappe
|
||||
|
||||
Hvis du installerer i en undermappe (f.eks. `yourdomain.com/pdf-tools/`):
|
||||
|
||||
1. Byg med den korrekte base-URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
2. Opret mappen i Hostinger:
|
||||
- Gå til **File Manager** → **public_html**
|
||||
- Opret en ny mappe: `pdf-tools`
|
||||
3. Upload hele indholdet af `dist` til `public_html/pdf-tools/`
|
||||
|
||||
## Trin 3: Opret .htaccess-filen
|
||||
|
||||
Opret en `.htaccess`-fil i roden af din installationsmappe (`public_html` eller `public_html/pdf-tools/`):
|
||||
|
||||
::: warning Vigtigt
|
||||
Ved installation i en undermappe skal du ændre `RewriteBase /` til `RewriteBase /pdf-tools/` (eller navnet på din mappe).
|
||||
:::
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Eksempel på .htaccess i en undermappe
|
||||
|
||||
For `yourdomain.com/pdf-tools/` skal du opdatere disse linjer:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### WASM-filer indlæses ikke
|
||||
|
||||
Sørg for, at MIME-typerne er sat korrekt i `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### LibreOffice-værktøjer virker ikke
|
||||
|
||||
Sikkerhedsheaderne er afgørende for SharedArrayBuffer:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
```
|
||||
|
||||
Hvis headerne ikke bliver anvendt, så kontakt Hostingers support for at aktivere `mod_headers`.
|
||||
|
||||
### 404-fejl ved genindlæsning af siden
|
||||
|
||||
Sørg for, at reglen for filtypen `.html` og sprogrutene er konfigureret korrekt. BentoPDF bruger statiske HTML-filer, ikke 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]
|
||||
```
|
||||
|
||||
### Grænser for filupload
|
||||
|
||||
Hostinger kan have grænser for filstørrelse. Upload WASM-filer separat, hvis masseupload mislykkes.
|
||||
@@ -0,0 +1,538 @@
|
||||
# Selvhostingsguide
|
||||
|
||||
BentoPDF kan selvhostes på din egen infrastruktur. Denne guide gennemgår forskellige deployment-muligheder.
|
||||
|
||||
## Hurtig start med Docker / Podman
|
||||
|
||||
Den hurtigste måde at selvhoste BentoPDF på:
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF leveres i to builds:
|
||||
>
|
||||
> - **Selvhostet build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Alle PDF-værktøjer, **uden** marketingelementerne (ingen hero, FAQ, udtalelser eller sidefod). Brug denne til interne deployments i teams og organisationer. Den er **ikke** en "lite"-version med færre funktioner.
|
||||
> - **Kommerciel build**: `ghcr.io/alam00000/bentopdf:latest`. Det fulde marketingsite, brugt af bentopdf.com selv og af kommercielle licensindehavere med offentligt tilgængelige deployments.
|
||||
>
|
||||
> Er du i tvivl: Hent det selvhostede build.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Konvertering af Office-filer kræver `SharedArrayBuffer`, hvilket betyder, at appen både skal være cross-origin-isoleret og serveres fra en sikker kontekst. Det officielle image sender allerede de nødvendige COOP/COEP-headere, men browsere deaktiverer stadig `SharedArrayBuffer` på almindelige HTTP-origins i lokale netværk som `http://192.168.x.x`.
|
||||
>
|
||||
> Brug kun `http://localhost` til test på samme enhed. Hvis brugerne tilgår BentoPDF via en LAN-IP eller et værtsnavn, skal du afslutte forbindelsen med 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
|
||||
```
|
||||
|
||||
Eller med 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)
|
||||
|
||||
Kør BentoPDF som en systemd-tjeneste. Opret `~/.config/containers/systemd/bentopdf.container`:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now bentopdf
|
||||
```
|
||||
|
||||
Se [Docker-deploymentguiden](/da/self-hosting/docker) for den fulde Quadlet-dokumentation.
|
||||
|
||||
## Bygning fra kildekode
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Konfigurationsindstillinger
|
||||
|
||||
### Selvhostet build (Simple Mode)
|
||||
|
||||
Det selvhostede build (imaget `bentopdf-simple`, også kaldet Simple Mode) er **funktionelt identisk** med det kommercielle build. Alle PDF-værktøjer er til stede og opfører sig på samme måde. Det skjuler blot de marketingelementer, der kun giver mening på bentopdf.com selv eller i en kommerciel, offentligt tilgængelig deployment. **Det er ikke en version med færre funktioner eller en "lite"-version.**
|
||||
|
||||
**Hvad det selvhostede build skjuler** (kun kosmetisk, ingen PDF-funktioner fjernes):
|
||||
|
||||
- Navigationslinje, hero-sektion, funktionssektion, FAQ, udtalelser, sidefod
|
||||
- Opdaterer sidens titel til "PDF-værktøjer"
|
||||
|
||||
**Hvad det selvhostede build beholder** (alt det, der faktisk arbejder med PDF):
|
||||
|
||||
- Alle PDF-værktøjer (flet, opdel, rediger, underskriv, OCR, Office-konvertering osv.)
|
||||
- Understøttelse af eget brand, al konfiguration ved build og under kørsel
|
||||
|
||||
Det kommercielle build (`ghcr.io/alam00000/bentopdf:latest`) driver bentopdf.com selv og bruges af kommercielle licensindehavere med offentligt tilgængelige deployments. Det tilføjer hero, FAQ, udtalelser og sidefod, som ikke ville give mening i et internt værktøj.
|
||||
|
||||
Hvis du selvhoster BentoPDF til dit team, din organisation eller som internt værktøj, skal du hente det selvhostede build:
|
||||
|
||||
```bash
|
||||
# Use the pre-built image (recommended)
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
|
||||
# Or build it yourself
|
||||
SIMPLE_MODE=true npm run build
|
||||
```
|
||||
|
||||
Se [SIMPLE_MODE.md](https://github.com/alam00000/bentopdf/blob/main/SIMPLE_MODE.md) for alle detaljer.
|
||||
|
||||
### Kommerciel build (offentligt tilgængelige deployments med dit eget brand)
|
||||
|
||||
Det kommercielle build (imaget `bentopdf`, uden suffikset `-simple`) driver bentopdf.com selv. Det indeholder det fulde marketingsite (hero, funktioner, FAQ, udtalelser, sidefod) oven på alle PDF-værktøjer. Brug dette build, når du vil **deploye BentoPDF som en offentligt tilgængelig PDF-tjeneste under dit eget brand**, for eksempel:
|
||||
|
||||
- Du kører BentoPDF som hostet SaaS for slutbrugere på dit eget domæne
|
||||
- Du vil have landingsside-oplevelsen (marketingsektioner + værktøjer), ikke kun selve værktøjsfladen
|
||||
- Du er kommerciel licensindehaver og indlejrer BentoPDF i et kommercielt produkt
|
||||
|
||||
**Kør det som det er** (BentoPDF-branding, nyttigt til at få vist buildet):
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
**Byg med dit eget brand** (den typiske kommercielle arbejdsgang):
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg VITE_BRAND_NAME="AcmePDF" \
|
||||
--build-arg VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
--build-arg VITE_FOOTER_TEXT="© 2026 Acme Corp. All rights reserved." \
|
||||
-t acmepdf .
|
||||
|
||||
docker run -p 3000:8080 acmepdf
|
||||
```
|
||||
|
||||
Alle andre build-indstillinger (`BASE_URL`, `VITE_DEFAULT_LANGUAGE`, `DISABLE_TOOLS`, WASM-URL-overstyringer osv.) virker på samme måde som i det selvhostede build.
|
||||
|
||||
::: warning Licensering
|
||||
Det er tilladt at køre det kommercielle build under begge af BentoPDF's to licensmuligheder:
|
||||
|
||||
- **AGPL-3.0** (gratis): tilladt, hvis din deployment udgiver hele sin kildekode under AGPL, inklusive eventuelle branding-ændringer og omkringliggende forretningslogik.
|
||||
- **Kommerciel licens** ($79 livstid): påkrævet til lukkede/proprietære deployments, hvor du ikke gør din branding-fork eller forretningskode open source.
|
||||
|
||||
Se [licenssiden](https://www.bentopdf.com/licensing) for den fulde sammenligning. AGPL-licenserede WASM-moduler (PyMuPDF, Ghostscript, CoherentPDF) indlæses fra et CDN under kørsel, så de indgår ikke i dit image og ændrer ikke din licensmæssige stilling.
|
||||
:::
|
||||
|
||||
### Base-URL
|
||||
|
||||
Deploy til en undermappe:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
### Eget brand
|
||||
|
||||
Erstat BentoPDF's standardlogo, -navn og sidefodstekst med dine egne ved build:
|
||||
|
||||
| Variabel | Beskrivelse | Standard |
|
||||
| ------------------ | ----------------------------------------- | --------------------------------------- |
|
||||
| `VITE_BRAND_NAME` | Brandnavn vist i sidehoved og sidefod | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logosti relativt til `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Brugerdefineret sidefods-/ophavsretstekst | `© 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
|
||||
```
|
||||
|
||||
Eller 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 .
|
||||
```
|
||||
|
||||
Branding virker i både fuld tilstand og Simple Mode og kan kombineres med alle andre build-indstillinger (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Deaktivering af bestemte værktøjer
|
||||
|
||||
Skjul enkelte værktøjer af hensyn til compliance eller sikkerhed. Deaktiverede værktøjer fjernes fra startsiden, søgning, genveje, workflow-builderen og direkte URL-adgang.
|
||||
|
||||
Værktøjs-id'er er sidens URL uden `.html`. Åbn et værktøj, og se på URL'en (f.eks. `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Ved build** (indbygget i bundlen):
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**Under kørsel** (ingen genopbygning nødvendig, montér en `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
|
||||
```
|
||||
|
||||
Begge metoder kan kombineres, og listerne bliver slået sammen.
|
||||
|
||||
Du kan også deaktivere bestemte funktioner i PDF-editoren (f.eks. redigering af indhold) uden at deaktivere hele værktøjet ved at tilføje `editorDisabledCategories` til `config.json`. Se [Docker-guiden](/da/self-hosting/docker#deaktivering-af-editorfunktioner) for den fulde liste over kategorier.
|
||||
|
||||
## Deployment-guider
|
||||
|
||||
Vælg din platform:
|
||||
|
||||
- [Vercel](/da/self-hosting/vercel)
|
||||
- [Netlify](/da/self-hosting/netlify)
|
||||
- [Cloudflare Pages](/da/self-hosting/cloudflare)
|
||||
- [AWS S3 + CloudFront](/da/self-hosting/aws)
|
||||
- [Hostinger](/da/self-hosting/hostinger)
|
||||
- [Nginx](/da/self-hosting/nginx)
|
||||
- [Apache](/da/self-hosting/apache)
|
||||
- [Docker](/da/self-hosting/docker)
|
||||
- [Kubernetes](/da/self-hosting/kubernetes)
|
||||
- [CORS-proxy](/da/self-hosting/cors-proxy) - Påkrævet til digitale signaturer
|
||||
|
||||
## Almindelige problemer
|
||||
|
||||
### Word / ODT / Excel / PowerPoint til PDF hænger (SharedArrayBuffer utilgængelig)
|
||||
|
||||
**Symptom**: LibreOffice-baserede dokumentkonverteringer (Word, ODT, Excel, PowerPoint til PDF) hænger ved ca. 55 % eller starter ikke. Browserens konsol kan vise:
|
||||
|
||||
> `ReferenceError: SharedArrayBuffer is not defined`
|
||||
|
||||
…eller `window.crossOriginIsolated` rapporterer `false`, eller WASM-kompileringen rapporterer `expected magic word 00 61 73 6d, found 1f 8b 08 08`.
|
||||
|
||||
**Årsag**: LibreOffice WASM kræver `SharedArrayBuffer`, som browseren kun aktiverer, når siden er **cross-origin-isoleret** OG serveres fra en **sikker kontekst**. Det betyder, at to ting skal være opfyldt:
|
||||
|
||||
1. Hvert svar indeholder begge headere:
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
2. Siden serveres fra `https://...` eller `http://localhost`. Almindelig HTTP på en LAN-IP (f.eks. `http://192.168.x.x`) tæller IKKE som sikker, og browsere deaktiverer `SharedArrayBuffer` dér.
|
||||
|
||||
Uoverensstemmelsen `00 61 73 6d / 1f 8b 08 08` er et separat undersymptom: De forkomprimerede `.wasm.gz`- / `.data.gz`-filer mangler svarheaderen `Content-Encoding: gzip`, så browseren modtager rå gzip-bytes i stedet for dekomprimeret WASM.
|
||||
|
||||
**Løsning**: Se din platformspecifikke deployment-guide for den præcise konfiguration:
|
||||
|
||||
- [Nginx →](/da/self-hosting/nginx#underskriv-pdf-eller-formularudfylder-viser-en-tom-læser-mjs-mime-fejl)
|
||||
- [Apache →](/da/self-hosting/apache#underskriv-pdf-eller-formularudfylder-viser-en-tom-viewer-mjs-mime-fejl)
|
||||
- [AWS S3 + CloudFront →](/da/self-hosting/aws#trin-3b-response-headers-policy-pakrævet-til-libreoffice-wasm)
|
||||
- [Cloudflare Pages →](/da/self-hosting/cloudflare#konfigurationsfil) (`_headers`-fil)
|
||||
- [Netlify →](/da/self-hosting/netlify#word-odt-excel-til-pdf-hænger-ved-55) (`netlify.toml`)
|
||||
- [Vercel →](/da/self-hosting/vercel#word-odt-excel-til-pdf-virker-ikke) (`vercel.json`)
|
||||
- [Hostinger →](/da/self-hosting/hostinger#libreoffice-værktøjer-virker-ikke) (`.htaccess`)
|
||||
- [Kubernetes →](/da/self-hosting/kubernetes#mjs-mime-typefejl-underskriv-pdf-formularudfylder-tom-iframe)
|
||||
- **Docker**: håndteres automatisk af den medfølgende nginx-konfiguration, ingen handling nødvendig.
|
||||
|
||||
**Kontrollér**: Åbn DevTools Console på en vilkårlig BentoPDF-side, og kør:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Hvis siden er HTTPS eller `http://localhost` OG begge COEP/COOP-headere findes på hvert svar, består begge kontroller. Hvis du er på `http://192.168.x.x` eller en anden HTTP-origin, der ikke er loopback, skal du afslutte den med HTTPS. Der findes ingen løsning, der kun bruger headere.
|
||||
|
||||
### `.mjs`-filer serveres som `application/octet-stream`
|
||||
|
||||
**Symptom**: Underskriv PDF / Formularudfylder / visse andre værktøjer viser en tom læser eller kan ikke indlæses. Browserens konsol rapporterer:
|
||||
|
||||
> `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.`
|
||||
|
||||
**Årsag**: Din webserver eller reverse proxy har ingen MIME-type-tilknytning til `.mjs`-filer (den indbyggede PDF-læser leverer ES-moduler med den filendelse). Mange standardkonfigurationer bruger som standard `application/octet-stream` til ukendte filendelser, og browsere nægter at køre dem som ES-moduler.
|
||||
|
||||
**Løsning**: Se din platformspecifikke deployment-guide for det præcise udsnit:
|
||||
|
||||
- [Nginx →](/da/self-hosting/nginx#underskriv-pdf-eller-formularudfylder-viser-en-tom-læser-mjs-mime-fejl)
|
||||
- [Apache →](/da/self-hosting/apache#underskriv-pdf-eller-formularudfylder-viser-en-tom-viewer-mjs-mime-fejl)
|
||||
- [AWS S3 + CloudFront →](/da/self-hosting/aws#trin-2-byg-og-upload) (se `aws s3 cp ... --include "*.mjs"`)
|
||||
- [Kubernetes →](/da/self-hosting/kubernetes#mjs-mime-typefejl-underskriv-pdf-formularudfylder-tom-iframe)
|
||||
- **Docker, Vercel, Netlify, Cloudflare Pages, Hostinger**: håndteres automatisk, ingen handling nødvendig.
|
||||
|
||||
**Kontrollér**: Åbn DevTools → fanen Network, find den mislykkede `.mjs`-anmodning, og kontrollér svarheaderen `Content-Type`. Den skal være `application/javascript`. Hvis den stadig er `application/octet-stream`, er det muligt, at en ydre reverse proxy eller et CDN gætter typen igen. Tjek hvert led i din serveringskæde.
|
||||
|
||||
## WASM-konfiguration (AGPL-komponenter)
|
||||
|
||||
BentoPDF **bundler ikke** AGPL-licenserede behandlingsbiblioteker i sin kildekode, men **forhåndskonfigurerer CDN-URL'er**, så alle funktioner virker ud af boksen, uden manuel opsætning.
|
||||
|
||||
::: tip Nul konfiguration som standard
|
||||
Fra og med v2.0.0 er WASM-moduler forhåndskonfigureret til at blive indlæst fra jsDelivr CDN via miljøvariabler. Alle avancerede funktioner virker med det samme uden brugerkonfiguration.
|
||||
:::
|
||||
|
||||
| Komponent | Licens | Funktioner |
|
||||
| --------------- | -------- | ------------------------------------------------------------------------------ |
|
||||
| **PyMuPDF** | AGPL-3.0 | EPUB/MOBI/FB2/XPS-konvertering, udtrækning af billeder, udtrækning af tabeller |
|
||||
| **Ghostscript** | AGPL-3.0 | PDF/A-konvertering, komprimering, udretning af skæve sider, rasterisering |
|
||||
| **CoherentPDF** | AGPL-3.0 | Indholdsfortegnelse, vedhæftninger, PDF-sammenfletning med bogmærker |
|
||||
|
||||
### Standardmiljøvariabler
|
||||
|
||||
Disse er sat i `.env.production` og indbygget i buildet:
|
||||
|
||||
```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=
|
||||
```
|
||||
|
||||
### Overstyring af WASM-URL'er
|
||||
|
||||
Du kan overstyre standardværdierne ved build til brugerdefinerede deployments:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
For at deaktivere et modul helt (kræver manuel brugerkonfiguration via Avancerede indstillinger) skal du sætte dets variabel til en tom streng.
|
||||
|
||||
Til OCR skal du enten lade alle `VITE_TESSERACT_*`-variabler være tomme og beholde de online standardressourcer, eller angive worker-, core- og lang-URL'erne sammen til selvhostet/offline OCR. Hvis du kun bundler bestemte OCR-sprog, skal du også sætte `VITE_TESSERACT_AVAILABLE_LANGUAGES` til de samme kommaseparerede koder, så brugerfladen kun tilbyder installerede sprog, og ikke-understøttede valg fejler med en beskrivende fejl. Til fuldt offline PDF-output med søgbar tekst skal du desuden sætte `VITE_OCR_FONT_BASE_URL` til den interne mappe, der serverer de bundlede OCR-skrifttyper.
|
||||
|
||||
Brugere kan også til enhver tid overstyre disse standardværdier via **Avancerede indstillinger** i brugerfladen. Brugeroverstyringer gemt i browseren har forrang over miljøstandarderne.
|
||||
|
||||
### Air-gapped / offline deployment
|
||||
|
||||
Til netværk uden internetadgang (offentlig sektor, sundhed, finans osv.). WASM-URL'erne er indbygget i JavaScript ved **build**. De egentlige WASM-filer downloades af **brugerens browser** under kørsel. Du skal derfor forberede alt på en maskine med internet og derefter overføre det til det isolerede netværk.
|
||||
|
||||
#### Automatiseret script (anbefalet)
|
||||
|
||||
Det medfølgende script `prepare-airgap.sh` automatiserer hele processen: det downloader WASM-pakker, bygger Docker-imaget og producerer en selvstændig bundle med et opsætningsscript.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Dette producerer en bundle-mappe:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Overfør bundlen til det air-gappede netværk via USB, internt artefaktlager eller en godkendt metode. Kør derefter det medfølgende opsætningsscript:
|
||||
|
||||
```bash
|
||||
cd bentopdf-airgap-bundle
|
||||
bash setup.sh
|
||||
```
|
||||
|
||||
Opsætningsscriptet indlæser Docker-imaget, udpakker WASM-filer og starter eventuelt containeren.
|
||||
|
||||
**Script-indstillinger:**
|
||||
|
||||
| Flag | Beskrivelse | Standard |
|
||||
| ------------------------------ | ------------------------------------------------------------- | -------------------------------------- |
|
||||
| `--wasm-base-url <url>` | Hvor WASM-filerne hostes internt | _(påkrævet, spørges om, hvis mangler)_ |
|
||||
| `--image-name <name>` | Docker image-tag | `bentopdf` |
|
||||
| `--output-dir <path>` | Output-mappe til bundlen | `./bentopdf-airgap-bundle` |
|
||||
| `--simple-mode` | Aktivér Simple Mode | fra |
|
||||
| `--base-url <path>` | Base-URL til undermappe (f.eks. `/pdf/`) | `/` |
|
||||
| `--language <code>` | Standardsprog i brugerfladen (f.eks. `fr`, `de`) | _(ingen)_ |
|
||||
| `--brand-name <name>` | Brugerdefineret brandnavn | _(ingen)_ |
|
||||
| `--brand-logo <path>` | Logosti relativt til `public/` | _(ingen)_ |
|
||||
| `--footer-text <text>` | Brugerdefineret sidefodstekst | _(ingen)_ |
|
||||
| `--ocr-languages <list>` | Kommasepareret liste over OCR-sprog, der skal bundles | `eng` |
|
||||
| `--list-ocr-languages` | Udskriv understøttede OCR-koder og navne, og afslut | fra |
|
||||
| `--search-ocr-language <term>` | Søg efter OCR-koder via navn eller forkortelse | fra |
|
||||
| `--dockerfile <path>` | Dockerfile, der skal bruges | `Dockerfile` |
|
||||
| `--skip-docker` | Spring Docker-build og eksport over | fra |
|
||||
| `--skip-wasm` | Spring WASM-download over (genbrug eksisterende `.tgz`-filer) | fra |
|
||||
|
||||
Den interaktive prompt accepterer også `list` for at udskrive den fulde liste over understøttede Tesseract-koder og `search <term>` for at finde match som `search german` eller `search chi`.
|
||||
|
||||
::: warning Krav om samme origin
|
||||
WASM-filer skal serveres fra **samme origin** som BentoPDF-appen. Web Workers bruger `importScripts()`, som ikke kan indlæse scripts på tværs af origins. Hvis BentoPDF for eksempel kører på `https://internal.example.com`, skal WASM-base-URL'en også være `https://internal.example.com/wasm`.
|
||||
:::
|
||||
|
||||
#### Manuelle trin
|
||||
|
||||
<details>
|
||||
<summary>Hvis du foretrækker at gøre det manuelt uden scriptet</summary>
|
||||
|
||||
**Trin 1: Download WASM- og OCR-pakkerne** (på en maskine med 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
|
||||
```
|
||||
|
||||
**Trin 2: Byg Docker-imaget med interne URL'er**
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
**Trin 3: Eksportér Docker-imaget**
|
||||
|
||||
```bash
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
```
|
||||
|
||||
**Trin 4: Overfør til det air-gappede netværk**
|
||||
|
||||
Kopiér via USB, internt artefaktlager eller en godkendt overførselsmetode:
|
||||
|
||||
- `bentopdf.tar`: Docker-imaget
|
||||
- De fem `.tgz` WASM/OCR-pakker fra trin 1
|
||||
- Mappen `tesseract-langdata/` fra trin 1
|
||||
- Mappen `ocr-fonts/` fra trin 1
|
||||
|
||||
**Trin 5: Opsætning inde i det air-gappede netværk**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Sørg for, at filerne er tilgængelige på de URL'er, du konfigurerede i trin 2, herunder `.../ocr/worker.min.js`, `.../ocr/core`, `.../ocr/lang-data` og `.../ocr/fonts`.
|
||||
|
||||
</details>
|
||||
|
||||
::: info Bygger du fra kildekode i stedet for Docker?
|
||||
Sæt variablerne i `.env.production`, før du kører `npm run build`:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Hosting af din egen WASM-proxy
|
||||
|
||||
Hvis du skal servere AGPL WASM-filer med korrekte CORS-headere, kan du deploye en simpel proxy. Se [Cloudflare WASM Proxy-guiden](https://github.com/alam00000/bentopdf/blob/main/cloudflare/WASM-PROXY.md) for et eksempel på en implementering.
|
||||
|
||||
::: tip Hvorfor adskilt?
|
||||
Denne adskillelse sikrer:
|
||||
|
||||
- Klar juridisk compliance for kommercielle brugere
|
||||
- BentoPDF's kerne forbliver under sin dobbeltlicens (AGPL-3.0 / kommerciel)
|
||||
- WASM-filer indlæses under kørsel og bundles ikke i kildekoden
|
||||
:::
|
||||
|
||||
## Systemkrav
|
||||
|
||||
| Krav | Minimum |
|
||||
| ---------- | --------------------------------- |
|
||||
| Lagerplads | ~100 MB (kerne uden AGPL-moduler) |
|
||||
| RAM | 512 MB |
|
||||
| CPU | Enhver moderne processor |
|
||||
|
||||
::: tip
|
||||
BentoPDF er et statisk site, så der kræves ingen database eller backend-server!
|
||||
:::
|
||||
@@ -0,0 +1,211 @@
|
||||
# Deploy med Kubernetes
|
||||
|
||||
Kubernetes er måske overkill til et statisk site, men det kan passe godt, hvis du allerede standardiserer på Helm + GitOps.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Påkrævede headere til konvertering af Office-filer**
|
||||
>
|
||||
> LibreOffice-baserede værktøjer (konvertering af Word, Excel, PowerPoint) kræver disse HTTP-headere for at understøtte `SharedArrayBuffer`:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> De officielle BentoPDF nginx-images indeholder disse headere. I Kubernetes er **Ingress-/Gateway-controllere også reverse proxies**, så sørg for, at disse headere bevares (eller tilføj dem i kanten).
|
||||
|
||||
## Forudsætninger
|
||||
|
||||
- Kubernetes-klynge
|
||||
- Helm v3
|
||||
- Et BentoPDF nginx-image (f.eks. `ghcr.io/alam00000/bentopdf:<tag>`), der serverer på **port 8080**
|
||||
|
||||
## Deploy med Helm
|
||||
|
||||
### Installér fra dette repo (lokalt 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
|
||||
```
|
||||
|
||||
### Installér fra GHCR (OCI-chart)
|
||||
|
||||
Hvis chartet er udgivet til GHCR som et OCI-artefakt:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Gør det tilgængeligt
|
||||
|
||||
### Port-forward (hurtig test)
|
||||
|
||||
```bash
|
||||
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
|
||||
```
|
||||
|
||||
### Ingress (valgfrit)
|
||||
|
||||
Aktivér Ingress (eksempel til nginx-ingress):
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: pdf.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Gateway API (valgfrit)
|
||||
|
||||
Dette chart understøtter Gateway API `Gateway` + `HTTPRoute`.
|
||||
|
||||
Eksempel (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
|
||||
```
|
||||
|
||||
## Sørg for, at SharedArrayBuffer-headerne stadig virker (Ingress/Gateway)
|
||||
|
||||
### Hvad der "burde" ske
|
||||
|
||||
BentoPDF's nginx-konfiguration sætter de nødvendige svarheadere. De fleste Ingress-/Gateway-controllere **sender upstream-svarheadere videre uændret**.
|
||||
|
||||
### Hvad der kan gå galt
|
||||
|
||||
- En controller- eller edge-politik, der **overskriver** eller **fjerner** svarheadere
|
||||
- En "security headers"-middleware, der sætter andre COOP/COEP-værdier
|
||||
|
||||
### Sådan kontrollerer du det
|
||||
|
||||
Kør dette mod dit offentlige endpoint:
|
||||
|
||||
```bash
|
||||
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
|
||||
```
|
||||
|
||||
Du skal se:
|
||||
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### Hvis din Ingress-controller ikke bevarer dem
|
||||
|
||||
Tilføj headerne i kanten (afhænger af controlleren). Eksempel til **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-typefejl (Underskriv PDF / Formularudfylder, tom iframe)
|
||||
|
||||
Hvis browserens konsol viser `Failed to load module script: ... non-JavaScript MIME type "application/octet-stream"` på en `.mjs`-anmodning, erstatter en Ingress-/Gateway-controller foran BentoPDF's nginx den upstream `Content-Type`-header med `application/octet-stream`.
|
||||
|
||||
BentoPDF-imagets nginx serverer allerede `.mjs` som `application/javascript`. Løsningen er at stoppe controlleren i at fjerne eller erstatte den. For nginx-ingress:
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
location ~* \.mjs$ {
|
||||
types {} default_type application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
Eller brug et `ResponseHeaderModifier`-filter i Gateway API til at sætte `Content-Type: application/javascript` for `.mjs`-stier. Kontrollér i DevTools → fanen Network: Den mislykkede `.mjs`-anmodning skulle nu vise `content-type: application/javascript`.
|
||||
|
||||
### Hvis du bruger Gateway API og vil tvinge headere på
|
||||
|
||||
Gateway API understøtter et `ResponseHeaderModifier`-filter. Du kan knytte det til i `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
|
||||
```
|
||||
|
||||
Understøttelse af bestemte filtre afhænger af din Gateway-controller. Hvis et filter ignoreres, så tilføj headerne i kanten eller i controller-laget i stedet.
|
||||
|
||||
## Deaktivering af bestemte værktøjer
|
||||
|
||||
Brug en ConfigMap til at deaktivere værktøjer under kørsel uden at genopbygge imaget:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Montér den i den serverede mappe:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Værktøjs-id'er er sidens URL uden `.html`. Åbn et værktøj, og se på URL'en (f.eks. `edit-pdf`, `merge-pdf`, `compress-pdf`). Deaktiverede værktøjer skjules fra startsiden, søgning, genveje, workflow-builderen og direkte URL-adgang. Se [Docker-guiden](/da/self-hosting/docker#deaktivering-af-bestemte-værktøjer) for den fulde liste over indstillinger.
|
||||
@@ -0,0 +1,139 @@
|
||||
# Deploy til Netlify
|
||||
|
||||
[Netlify](https://netlify.com) tilbyder fremragende hosting af statiske sites med en generøs gratis plan.
|
||||
|
||||
## Deploy med ét klik
|
||||
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Manuel deployment
|
||||
|
||||
### Trin 1: Forbind repository
|
||||
|
||||
1. Log ind på [Netlify](https://app.netlify.com)
|
||||
2. Klik på "Add new site" → "Import an existing project"
|
||||
3. Forbind din GitHub-konto
|
||||
4. Vælg din BentoPDF-fork
|
||||
|
||||
### Trin 2: Konfigurér build-indstillinger
|
||||
|
||||
| Indstilling | Værdi |
|
||||
| ----------------- | --------------- |
|
||||
| Build command | `npm run build` |
|
||||
| Publish directory | `dist` |
|
||||
| Node version | 18+ |
|
||||
|
||||
### Trin 3: Deploy
|
||||
|
||||
Klik på "Deploy site", og vent på buildet.
|
||||
|
||||
## Konfigurationsfil
|
||||
|
||||
Opret `netlify.toml` i dit projekts rodmappe:
|
||||
|
||||
```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 Vigtigt
|
||||
Headerne `Cross-Origin-Embedder-Policy` og `Cross-Origin-Opener-Policy` er påkrævet til konvertering af Word/ODT/Excel/PowerPoint til PDF. Uden dem er `SharedArrayBuffer` utilgængelig, og LibreOffice WASM-motoren kan ikke starte.
|
||||
|
||||
Headerne `Content-Encoding: gzip` på filerne `.wasm.gz` og `.data.gz` får browseren til at dekomprimere dem automatisk. Uden dem modtager browseren rå gzip-bytes, og WASM-kompileringen mislykkes.
|
||||
:::
|
||||
|
||||
## Miljøvariabler
|
||||
|
||||
Angiv disse under Site settings → Environment variables:
|
||||
|
||||
| Variabel | Beskrivelse |
|
||||
| ----------------------- | ------------------------------------------------------------ |
|
||||
| `SIMPLE_MODE` | Sæt til `true` for et minimalt build |
|
||||
| `VITE_BRAND_NAME` | Brugerdefineret brandnavn (erstatter "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logosti relativt til `public/` (f.eks. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Brugerdefineret sidefods-/ophavsretstekst |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standardsprog i brugerfladen (f.eks. `fr`, `de`, `es`) |
|
||||
|
||||
## Eget domæne
|
||||
|
||||
1. Gå til Site settings → Domain management
|
||||
2. Klik på "Add custom domain"
|
||||
3. Følg anvisningerne til DNS-konfiguration
|
||||
|
||||
## Store mediefiler
|
||||
|
||||
Til store WASM-filer kan du overveje at aktivere [Netlify Large Media](https://docs.netlify.com/large-media/overview/):
|
||||
|
||||
```bash
|
||||
netlify lm:setup
|
||||
git lfs track "*.wasm"
|
||||
```
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### Word/ODT/Excel til PDF hænger ved 55 %
|
||||
|
||||
Hvis dokumentkonverteringer hænger ved 55 %, så åbn DevTools Console og kontrollér:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Hvis `crossOriginIsolated` er `false`, bliver COEP/COOP-headerne fra din `netlify.toml` ikke anvendt. Sørg for, at filen ligger i dit projekts rodmappe, og deploy igen.
|
||||
|
||||
Hvis du ser `expected magic word 00 61 73 6d, found 1f 8b 08 08` i konsollen, mangler `.wasm.gz`-filerne headerne `Content-Encoding: gzip`. Sørg for, at blokkene `[[headers]]` til `soffice.wasm.gz` og `soffice.data.gz` findes i din `netlify.toml`.
|
||||
|
||||
### Build mislykkes
|
||||
|
||||
Kontrollér kompatibiliteten med Node-versionen:
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
```
|
||||
|
||||
### Langsom første indlæsning
|
||||
|
||||
Aktivér asset-optimering under Site settings → Build & deploy → Asset optimization.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Deploy med Nginx
|
||||
|
||||
Host BentoPDF på din egen server med Nginx.
|
||||
|
||||
## Forudsætninger
|
||||
|
||||
- Ubuntu/Debian-server
|
||||
- Nginx installeret
|
||||
- SSL-certifikat (anbefalet: Let's Encrypt)
|
||||
|
||||
## Trin 1: Byg projektet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
For at tilpasse branding skal du sætte miljøvariabler, før du bygger:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Trin 2: Kopiér filer
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Trin 3: Nginx-konfiguration
|
||||
|
||||
Opret `/etc/nginx/sites-available/bentopdf`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
||||
|
||||
root /var/www/bentopdf;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/wasm;
|
||||
gzip_min_length 1000;
|
||||
|
||||
# WASM MIME type
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
|
||||
# Required headers for SharedArrayBuffer (LibreOffice WASM)
|
||||
# These must be on every response - especially HTML pages
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Pre-compressed LibreOffice WASM binary
|
||||
location ~* /libreoffice-wasm/soffice\.wasm\.gz$ {
|
||||
gzip off;
|
||||
types {} default_type application/wasm;
|
||||
add_header Content-Encoding gzip;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# Pre-compressed LibreOffice WASM data
|
||||
location ~* /libreoffice-wasm/soffice\.data\.gz$ {
|
||||
gzip off;
|
||||
types {} default_type application/octet-stream;
|
||||
add_header Content-Encoding gzip;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|wasm)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
|
||||
# SPA routing - serve index.html for all routes
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Trin 4: Aktivér sitet
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Trin 5: SSL med Let's Encrypt
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
## Deployment i undermappe
|
||||
|
||||
For at hoste på `/pdf/`:
|
||||
|
||||
```nginx
|
||||
location /pdf/ {
|
||||
alias /var/www/bentopdf/;
|
||||
try_files $uri $uri/ /pdf/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
Byg med:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
## Ydelsesjustering
|
||||
|
||||
Tilføj til `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;
|
||||
}
|
||||
```
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### WASM indlæses ikke
|
||||
|
||||
Sørg for, at MIME-typen er sat:
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
### Underskriv PDF eller Formularudfylder viser en tom læser (`.mjs` MIME-fejl)
|
||||
|
||||
Hvis browserens konsol viser:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…og den mislykkede anmodning ender på `.mjs`, mangler din nginx tilknytningen `.mjs → application/javascript`. Den indbyggede PDF-læser (brugt af Underskriv PDF, Formularudfylder og et par andre værktøjer) leverer ES-modulfiler med filendelsen `.mjs`, og nginx' standardfil `mime.types` indeholder dem ikke som standard.
|
||||
|
||||
Ret det ved at tilføje `mjs` til den eksisterende `application/javascript`-linje i `/etc/nginx/mime.types`:
|
||||
|
||||
```nginx
|
||||
application/javascript js mjs;
|
||||
```
|
||||
|
||||
> Tilføj IKKE en _anden_ `application/javascript`-linje. Nginx læser `mime.types` linje for linje, og kun én tilknytning mellem type og filendelse vinder, så en anden linje ignoreres i stilhed på nogle versioner. Redigér den eksisterende linje, så `mjs` står ved siden af `js`.
|
||||
|
||||
Eller overstyr lokalt i din server-blok (uden at redigere den globale fil):
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
Genindlæs nginx efter ændringen:
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
For at bekræfte det: Find den mislykkede `.mjs`-anmodning i browserens DevTools → fanen Network, og kontrollér svarheaderen `Content-Type`. Den skulle nu være `application/javascript`, ikke `application/octet-stream`.
|
||||
|
||||
### Word/ODT/Excel til PDF virker ikke
|
||||
|
||||
LibreOffice WASM kræver `SharedArrayBuffer`, som kræver headerne `Cross-Origin-Embedder-Policy` og `Cross-Origin-Opener-Policy`. Det kræver også en sikker kontekst, så `http://localhost` virker til lokal test, men `http://192.168.x.x` eller andre LAN-IP'er kræver normalt HTTPS. Bemærk, at nginx' `add_header`-direktiver i en `location`-blok **overskriver** `add_header`-direktiver på serverniveau. De bliver ikke slået sammen. Hver `location`-blok med sin egen `add_header` skal indeholde COEP/COOP-headerne.
|
||||
|
||||
Kontrollér med:
|
||||
|
||||
```bash
|
||||
curl -I https://your-domain.com/ | grep -i cross-origin
|
||||
```
|
||||
|
||||
Hvis du bruger en reverse proxy foran nginx, så sørg for, at den bevarer disse headere.
|
||||
|
||||
### 502 Bad Gateway
|
||||
|
||||
Tjek Nginx' fejllogge:
|
||||
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
# Deploy til Vercel
|
||||
|
||||
[Vercel](https://vercel.com) tilbyder den letteste deployment-oplevelse for statiske sites.
|
||||
|
||||
## Deploy med ét klik
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Manuel deployment
|
||||
|
||||
### Trin 1: Fork repositoriet
|
||||
|
||||
Fork [bentopdf/bentopdf](https://github.com/alam00000/bentopdf) til din GitHub-konto.
|
||||
|
||||
### Trin 2: Importér til Vercel
|
||||
|
||||
1. Gå til [vercel.com/new](https://vercel.com/new)
|
||||
2. Vælg dit forkede repository
|
||||
3. Konfigurér projektet:
|
||||
|
||||
| Indstilling | Værdi |
|
||||
| ---------------- | --------------- |
|
||||
| Framework Preset | Vite |
|
||||
| Build Command | `npm run build` |
|
||||
| Output Directory | `dist` |
|
||||
| Install Command | `npm install` |
|
||||
|
||||
### Trin 3: Miljøvariabler (valgfrit)
|
||||
|
||||
Tilføj disse, hvis det er nødvendigt:
|
||||
|
||||
| Variabel | Beskrivelse |
|
||||
| ----------------------- | ------------------------------------------------------------ |
|
||||
| `SIMPLE_MODE` | Sæt til `true` for minimal brugerflade |
|
||||
| `VITE_BRAND_NAME` | Brugerdefineret brandnavn (erstatter "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Logosti relativt til `public/` (f.eks. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Brugerdefineret sidefods-/ophavsretstekst |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Standardsprog i brugerfladen (f.eks. `fr`, `de`, `es`) |
|
||||
|
||||
### Trin 4: Deploy
|
||||
|
||||
Klik på "Deploy", og vent på, at buildet er færdigt.
|
||||
|
||||
## Eget domæne
|
||||
|
||||
1. Gå til dit projekts indstillinger
|
||||
2. Naviger til "Domains"
|
||||
3. Tilføj dit eget domæne
|
||||
4. Konfigurér DNS som anvist
|
||||
|
||||
## Begrænsninger
|
||||
|
||||
::: warning Store filer
|
||||
Vercels serverless-funktioner har en grænse på 50 MB. Da BentoPDF er et statisk site, bør det ikke påvirke dig, men WASM-modulerne er store (ca. 100 MB i alt). Sørg for, at de leveres fra mappen `/public`.
|
||||
:::
|
||||
|
||||
## Fejlfinding
|
||||
|
||||
### Build-timeout
|
||||
|
||||
Hvis buildet timer ud, så prøv:
|
||||
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 ved opdatering af siden
|
||||
|
||||
Tilføj en `vercel.json` til SPA-routing:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
```
|
||||
|
||||
### Word/ODT/Excel til PDF virker ikke
|
||||
|
||||
LibreOffice WASM-konverteringer kræver `SharedArrayBuffer`, som kræver disse svarheadere på alle sider:
|
||||
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
|
||||
De forkomprimerede `.wasm.gz`- og `.data.gz`-filer skal også have `Content-Encoding: gzip`, så browseren dekomprimerer dem.
|
||||
|
||||
Tilføj disse til din `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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For at kontrollere det skal du åbne DevTools Console og køre `console.log(window.crossOriginIsolated)`. Den skal returnere `true`.
|
||||
Reference in new issue
Block a user