mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-13 09:54:19 +01:00
feat: translate all pages, UI messages and docs and fix SEO redirects and indexing
This commit is contained in:
1 parent
5e019de5ae
commit
4edb1718cc
3344 files changed
+415643
-4759
No files matched your search
@@ -0,0 +1,275 @@
|
||||
# Déployer avec Apache
|
||||
|
||||
Hébergez BentoPDF avec Apache HTTP Server.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Apache 2.4+
|
||||
- mod_rewrite activé
|
||||
- Certificat SSL (recommandé)
|
||||
|
||||
## Étape 1 : compiler le projet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Pour personnaliser l'image de marque, définissez des variables d'environnement avant la compilation :
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Étape 2 : copier les fichiers
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Étape 3 : configuration d'Apache
|
||||
|
||||
Créez `/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>
|
||||
```
|
||||
|
||||
## Étape 4 : .htaccess pour le routage
|
||||
|
||||
Créez `/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
|
||||
```
|
||||
|
||||
## Étape 5 : activer les modules requis
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
sudo a2enmod ssl
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod expires
|
||||
sudo a2enmod deflate
|
||||
```
|
||||
|
||||
## Étape 6 : activer le site
|
||||
|
||||
```bash
|
||||
sudo a2ensite bentopdf.conf
|
||||
sudo apache2ctl configtest
|
||||
sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
## Déploiement dans un sous-répertoire
|
||||
|
||||
Pour héberger sous `/pdf/` :
|
||||
|
||||
1. Compilez avec l'URL de base :
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
2. Mettez à jour `.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
|
||||
```
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Erreurs 404 sur les fichiers WASM
|
||||
|
||||
Vérifiez que le type MIME est configuré :
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Signer un PDF ou le remplissage de formulaires affiche un visualiseur vide (erreur MIME `.mjs`)
|
||||
|
||||
Si la console du navigateur affiche :
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…et que la requête en échec se termine par `.mjs`, il manque à votre configuration Apache l'association `.mjs → application/javascript`. Le visualiseur PDF fourni livre des modules ES avec l'extension `.mjs`, et le fichier `mime.types` standard d'Apache ne les inclut pas par défaut.
|
||||
|
||||
Ajoutez à votre VirtualHost ou à `.htaccess` :
|
||||
|
||||
```apache
|
||||
AddType application/javascript .mjs
|
||||
```
|
||||
|
||||
Rechargez ensuite Apache :
|
||||
|
||||
```bash
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
Pour vérifier : dans les DevTools du navigateur → onglet Réseau, trouvez la requête `.mjs` en échec et consultez l'en-tête de réponse `Content-Type`. Il doit maintenant valoir `application/javascript`, et non `application/octet-stream`.
|
||||
|
||||
### La réécriture ne fonctionne pas
|
||||
|
||||
Vérifiez que mod_rewrite est activé :
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
### La conversion Word/ODT/Excel vers PDF ne fonctionne pas
|
||||
|
||||
LibreOffice WASM nécessite `SharedArrayBuffer`, qui exige ces en-têtes :
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
Un contexte sécurisé est également nécessaire. `http://localhost` fonctionne pour les tests locaux, mais `http://192.168.x.x` ou d'autres adresses IP du réseau local nécessitent généralement HTTPS. Si les en-têtes sont présents mais que `window.crossOriginIsolated` vaut toujours `false`, vérifiez si la page est ouverte en HTTP simple sur une origine autre que le bouclage local.
|
||||
|
||||
Les fichiers précompressés `.wasm.gz` et `.data.gz` ont aussi besoin du bon `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>
|
||||
```
|
||||
|
||||
Vérifiez que `mod_headers` est activé : `sudo a2enmod headers`
|
||||
|
||||
### Permission refusée
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
sudo chmod -R 755 /var/www/bentopdf
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
# Déployer sur AWS S3 + CloudFront
|
||||
|
||||
Hébergez BentoPDF sur AWS pour un contrôle et une évolutivité maximaux.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
User → CloudFront (CDN) → S3 (Static Files)
|
||||
```
|
||||
|
||||
## Étape 1 : créer le bucket S3
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Étape 2 : compiler et téléverser
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Étape 3 : créer la distribution CloudFront
|
||||
|
||||
```bash
|
||||
aws cloudfront create-distribution \
|
||||
--origin-domain-name your-bentopdf-bucket.s3.amazonaws.com \
|
||||
--default-root-object index.html
|
||||
```
|
||||
|
||||
Ou utilisez la console AWS :
|
||||
|
||||
1. Allez dans CloudFront → Create distribution
|
||||
2. Origin domain : sélectionnez votre bucket S3
|
||||
3. Activez « Origin Access Control »
|
||||
4. Default root object : `index.html`
|
||||
5. Créez la distribution
|
||||
|
||||
## Étape 3b : politique d'en-têtes de réponse (requise pour LibreOffice WASM)
|
||||
|
||||
Les conversions basées sur LibreOffice (Word, Excel, PowerPoint vers PDF) nécessitent `SharedArrayBuffer`, qui exige des en-têtes de réponse spécifiques. Créez une politique d'en-têtes de réponse CloudFront :
|
||||
|
||||
1. Allez dans CloudFront → Policies → Response headers
|
||||
2. Créez une politique personnalisée avec ces en-têtes :
|
||||
|
||||
| Header | Value |
|
||||
| ------------------------------ | -------------- |
|
||||
| `Cross-Origin-Embedder-Policy` | `require-corp` |
|
||||
| `Cross-Origin-Opener-Policy` | `same-origin` |
|
||||
| `Cross-Origin-Resource-Policy` | `cross-origin` |
|
||||
|
||||
3. Associez la politique au comportement de cache par défaut de votre distribution
|
||||
|
||||
Ou via la 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}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Étape 3c : métadonnées S3 pour les fichiers WASM précompressés
|
||||
|
||||
Les fichiers WASM de LibreOffice sont précompressés (`.wasm.gz`, `.data.gz`). Définissez le bon Content-Type et le bon Content-Encoding pour que les navigateurs les décompressent :
|
||||
|
||||
```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 Important
|
||||
Sans la politique d'en-têtes de réponse, `SharedArrayBuffer` n'est pas disponible et les conversions LibreOffice WASM resteront bloquées à environ 55 %. Sans le bon Content-Encoding sur les fichiers `.gz`, le navigateur reçoit des octets gzip bruts et la compilation WASM échoue.
|
||||
:::
|
||||
|
||||
## Étape 4 : politique du bucket S3
|
||||
|
||||
Autorisez CloudFront à accéder au bucket :
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowCloudFrontAccess",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "cloudfront.amazonaws.com"
|
||||
},
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::your-bentopdf-bucket/*",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Étape 5 : pages d'erreur personnalisées
|
||||
|
||||
Configurez la 404 pour renvoyer `index.html` afin de gérer le routage SPA :
|
||||
|
||||
1. CloudFront → Error pages
|
||||
2. Créez une réponse d'erreur personnalisée :
|
||||
- HTTP error code : 404
|
||||
- Response page path : `/index.html`
|
||||
- HTTP response code : 200
|
||||
|
||||
## Estimation des coûts
|
||||
|
||||
| Ressource | Coût estimé |
|
||||
| ----------------------------- | ------------ |
|
||||
| Stockage S3 (~500 Mo) | ~0,01 $/mois |
|
||||
| CloudFront (1 To transféré) | ~85 $/mois |
|
||||
| CloudFront (10 Go transférés) | ~0,85 $/mois |
|
||||
|
||||
::: tip
|
||||
Utilisez S3 Intelligent Tiering pour optimiser les coûts des fichiers rarement consultés.
|
||||
:::
|
||||
|
||||
## Automatisation avec 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 @@
|
||||
# Déployer sur Cloudflare Pages
|
||||
|
||||
[Cloudflare Pages](https://pages.cloudflare.com) propose un hébergement de sites statiques rapide et mondial, avec une bande passante illimitée.
|
||||
|
||||
## Déploiement rapide
|
||||
|
||||
1. Allez sur [Cloudflare Pages](https://dash.cloudflare.com/?to=/:account/pages)
|
||||
2. Cliquez sur « Create a project »
|
||||
3. Connectez votre dépôt GitHub
|
||||
|
||||
## Configuration du build
|
||||
|
||||
| Paramètre | Valeur |
|
||||
| ---------------------- | --------------- |
|
||||
| Framework preset | None |
|
||||
| Build command | `npm run build` |
|
||||
| Build output directory | `dist` |
|
||||
| Root directory | `/` |
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
Ajoutez-les dans Settings → Environment variables :
|
||||
|
||||
| Variable | Valeur |
|
||||
| ----------------------- | ---------------------------------------------------------- |
|
||||
| `NODE_VERSION` | `18` |
|
||||
| `SIMPLE_MODE` | `false` (facultatif) |
|
||||
| `VITE_BRAND_NAME` | Nom de marque personnalisé (facultatif) |
|
||||
| `VITE_BRAND_LOGO` | Chemin du logo relatif à `public/` (facultatif) |
|
||||
| `VITE_FOOTER_TEXT` | Texte de pied de page/copyright personnalisé (facultatif) |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Langue par défaut de l'interface, p. ex. `fr` (facultatif) |
|
||||
|
||||
## Fichier de configuration
|
||||
|
||||
Créez `_headers` dans votre dossier `public` :
|
||||
|
||||
```
|
||||
# Required security headers for SharedArrayBuffer (used by LibreOffice WASM)
|
||||
/*
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
||||
|
||||
# Pre-compressed LibreOffice WASM binary
|
||||
/libreoffice-wasm/soffice.wasm.gz
|
||||
Content-Type: application/wasm
|
||||
Content-Encoding: gzip
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
# Pre-compressed LibreOffice WASM data
|
||||
/libreoffice-wasm/soffice.data.gz
|
||||
Content-Type: application/octet-stream
|
||||
Content-Encoding: gzip
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
# Cache WASM files aggressively
|
||||
/*.wasm
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
Content-Type: application/wasm
|
||||
|
||||
# Service worker
|
||||
/sw.js
|
||||
Cache-Control: no-cache
|
||||
```
|
||||
|
||||
::: warning Important
|
||||
Les en-têtes `Cross-Origin-Embedder-Policy` et `Cross-Origin-Opener-Policy` sont requis pour les conversions Word/ODT/Excel/PowerPoint vers PDF. Sans eux, `SharedArrayBuffer` n'est pas disponible et le moteur LibreOffice WASM ne parviendra pas à s'initialiser.
|
||||
:::
|
||||
|
||||
Créez `_redirects` pour le routage SPA :
|
||||
|
||||
```
|
||||
/* /index.html 200
|
||||
```
|
||||
|
||||
## Domaine personnalisé
|
||||
|
||||
1. Ouvrez votre projet Pages
|
||||
2. Cliquez sur « Custom domains »
|
||||
3. Ajoutez votre domaine
|
||||
4. Cloudflare configurera automatiquement le DNS si le domaine est chez Cloudflare
|
||||
|
||||
## Avantages
|
||||
|
||||
- **Bande passante illimitée et gratuite**
|
||||
- **CDN mondial** avec plus de 300 points de présence
|
||||
- **HTTPS automatique**
|
||||
- **Déploiements de prévisualisation** pour les pull requests
|
||||
- **Builds rapides**
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Téléversement de gros fichiers
|
||||
|
||||
Cloudflare Pages prend en charge les fichiers jusqu'à 25 Mo. Les modules WASM ne devraient pas poser de problème, mais si vous atteignez les limites, envisagez :
|
||||
|
||||
```bash
|
||||
# Split large files during build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Limites de taille des Workers
|
||||
|
||||
Si vous utilisez Cloudflare Workers pour un routage avancé, notez la limite de 1 Mo des offres gratuites.
|
||||
|
||||
## Worker proxy CORS (pour les signatures numériques)
|
||||
|
||||
L'outil de signature numérique nécessite un proxy CORS pour récupérer les chaînes de certificats. Déployez le worker fourni :
|
||||
|
||||
```bash
|
||||
cd cloudflare
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Fonctionnalités de sécurité
|
||||
|
||||
| Fonctionnalité | Description |
|
||||
| -------------------------- | --------------------------------------------- |
|
||||
| **Restrictions d'URL** | Seules les URL de certificats sont autorisées |
|
||||
| **Limite de taille** | 10 Mo max par requête |
|
||||
| **Limitation de débit** | 60 req/IP/min (nécessite KV) |
|
||||
| **Blocage des IP privées** | Bloque localhost et les IP internes |
|
||||
|
||||
### Activer la limitation de débit
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Compiler avec l'URL du proxy
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
Ou avec 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 .
|
||||
```
|
||||
|
||||
> **Remarque :** Consultez le [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required) pour la configuration de la signature HMAC.
|
||||
@@ -0,0 +1,180 @@
|
||||
# Proxy CORS pour la récupération des certificats
|
||||
|
||||
L'outil de signature numérique utilise un proxy CORS pour récupérer les certificats des émetteurs auprès d'autorités de certification (AC) externes. C'est nécessaire, car de nombreux serveurs d'AC n'incluent pas d'en-têtes CORS dans leurs réponses, ce qui empêche la récupération directe depuis le navigateur.
|
||||
|
||||
De plus, de nombreux serveurs d'AC diffusent leurs certificats en HTTP simple. Lorsque votre instance BentoPDF est servie en HTTPS, les navigateurs bloquent ces requêtes HTTP (politique de contenu mixte). Le proxy CORS résout ces deux problèmes en faisant transiter les requêtes par un point de terminaison HTTPS avec les bons en-têtes.
|
||||
|
||||
## Fonctionnement
|
||||
|
||||
Lors de la signature d'un PDF avec un certificat :
|
||||
|
||||
1. La bibliothèque `zgapdfsigner` tente de construire une chaîne de certificats complète
|
||||
2. Elle récupère les certificats des émetteurs à partir des URL intégrées à l'extension AIA (Authority Information Access) de votre certificat
|
||||
3. Ces requêtes passent par un proxy CORS qui ajoute les en-têtes `Access-Control-Allow-Origin` nécessaires
|
||||
4. Le proxy renvoie les données du certificat au navigateur
|
||||
|
||||
## Auto-héberger le proxy CORS
|
||||
|
||||
Si vous auto-hébergez BentoPDF, vous devrez déployer votre propre proxy CORS pour que les signatures numériques fonctionnent avec les certificats qui nécessitent la récupération de la chaîne.
|
||||
|
||||
### Option 1 : Cloudflare Workers (recommandé)
|
||||
|
||||
1. **Installez la CLI Wrangler** :
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
```
|
||||
|
||||
2. **Connectez-vous à Cloudflare** :
|
||||
|
||||
```bash
|
||||
wrangler login
|
||||
```
|
||||
|
||||
3. **Clonez BentoPDF et mettez à jour les origines autorisées** :
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf/cloudflare
|
||||
```
|
||||
|
||||
Ouvrez `cors-proxy-worker.js` et remplacez le tableau `ALLOWED_ORIGINS` par votre domaine :
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = [
|
||||
'https://your-domain.com',
|
||||
'https://www.your-domain.com',
|
||||
];
|
||||
```
|
||||
|
||||
::: warning Important
|
||||
Sans cette modification, le proxy rejettera toutes les requêtes de votre site avec une erreur **403 Forbidden**. Par défaut, seules les requêtes provenant de `bentopdf.com` sont autorisées.
|
||||
:::
|
||||
|
||||
4. **Déployez le proxy** :
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Notez l'URL de votre worker (p. ex. `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
|
||||
|
||||
5. **Recompilez BentoPDF avec l'URL du proxy** :
|
||||
|
||||
Avec 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 .
|
||||
```
|
||||
|
||||
Avec une compilation depuis les sources :
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
### Option 2 : proxy backend personnalisé
|
||||
|
||||
Vous pouvez aussi créer votre propre point de terminaison proxy. Les exigences sont les suivantes :
|
||||
|
||||
1. Accepter les requêtes GET avec un paramètre de requête `url`
|
||||
2. Récupérer l'URL depuis votre serveur (aucune restriction CORS côté serveur)
|
||||
3. Renvoyer la réponse avec ces en-têtes :
|
||||
- `Access-Control-Allow-Origin: https://your-domain.com`
|
||||
- `Access-Control-Allow-Methods: GET, OPTIONS`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
|
||||
Exemple d'implémentation avec 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' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Considérations de sécurité
|
||||
|
||||
Le Cloudflare Worker fourni intègre plusieurs mesures de sécurité :
|
||||
|
||||
| Fonctionnalité | Description |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| **Validation de l'origine** | Autorise uniquement les requêtes des domaines listés dans `ALLOWED_ORIGINS` |
|
||||
| **Restrictions d'URL** | Autorise uniquement les URL de certificats (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`) |
|
||||
| **Blocage des IP privées** | Bloque les plages privées IPv4/IPv6, link-local, bouclage, IP décimales et métadonnées cloud |
|
||||
| **Sûreté du Content-Type** | Ne renvoie que des types MIME de certificat sûrs, bloque l'injection de content-type en amont |
|
||||
| **Limite de taille** | Réponse en flux limitée à 10 Mo, interrompue en cours de téléchargement en cas de dépassement |
|
||||
| **Limitation de débit** | 60 requêtes par minute par IP ou IPv6 /64 (nécessite KV, laisse passer en cas d'erreur KV) |
|
||||
| **Signatures HMAC** | Signature côté client facultative (dissuade les abus occasionnels) |
|
||||
|
||||
### DNS rebinding (auto-hébergement hors Cloudflare)
|
||||
|
||||
Le worker rejette toute URL qui se résout vers une IP privée ou interne. Il ne peut pas utiliser une liste fixe d'hôtes autorisés, car les URL des chaînes de certificats pointent vers l'AC qui a émis le certificat (FNMT, AC d'entreprise, etc.), donc les destinations ne sont pas connues à l'avance. Il reste une petite fenêtre de rebinding : une personne qui gère le DNS de son propre domaine pourrait renvoyer une IP publique lors du contrôle de sécurité, puis une IP privée pour la vraie requête un instant plus tard.
|
||||
|
||||
Sur Cloudflare, ce n'est pas exploitable : les Workers ne peuvent pas atteindre les IP privées ni le point de terminaison de métadonnées `169.254.169.254`, donc un rebinding n'aboutit à rien. Ne donnez simplement pas au worker de voie d'accès : n'associez ni Tunnel, ni Service, ni binding de réseau privé (il n'a besoin que de `RATE_LIMIT_KV`).
|
||||
|
||||
Si vous l'exécutez hors de Cloudflare (workerd, Node, etc.) dans un VPC, le runtime _peut_ atteindre les IP internes. Placez un pare-feu de sortie devant lui et bloquez les plages privées : `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` et leurs équivalents IPv6. Le code de l'application ne peut pas l'imposer une fois hors de Cloudflare.
|
||||
|
||||
## Désactiver le proxy
|
||||
|
||||
Si vous ne souhaitez pas utiliser de proxy CORS, définissez la variable d'environnement sur une chaîne vide :
|
||||
|
||||
```
|
||||
VITE_CORS_PROXY_URL=
|
||||
```
|
||||
|
||||
**Remarque** : sans le proxy, la signature avec des certificats qui nécessitent la récupération externe de la chaîne (comme FNMT ou certaines AC d'entreprise) échouera avec une erreur « Failed to fetch ».
|
||||
|
||||
## Dépannage
|
||||
|
||||
### « Signing error: TypeError: Failed to fetch »
|
||||
|
||||
Cela signifie généralement :
|
||||
|
||||
1. **Aucun proxy CORS configuré** : définissez `VITE_CORS_PROXY_URL` et recompilez
|
||||
2. **Contenu mixte bloqué** : votre site est en HTTPS mais l'URL de l'émetteur du certificat est en HTTP. Le proxy CORS résout ce problème.
|
||||
3. **Le proxy CORS rejette votre origine** : vérifiez que votre domaine figure dans le tableau `ALLOWED_ORIGINS` de `cors-proxy-worker.js`
|
||||
|
||||
### « 403 Forbidden » renvoyé par le proxy
|
||||
|
||||
Votre domaine ne figure pas dans la liste `ALLOWED_ORIGINS`. Modifiez `cors-proxy-worker.js` :
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = ['https://your-domain.com'];
|
||||
```
|
||||
|
||||
Redéployez ensuite : `npx wrangler deploy`
|
||||
|
||||
### Tester le proxy
|
||||
|
||||
```bash
|
||||
curl -H "Origin: https://your-domain.com" \
|
||||
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"
|
||||
```
|
||||
|
||||
### Certificats qui fonctionnent sans proxy
|
||||
|
||||
Certains certificats incluent la chaîne complète dans le fichier P12/PFX et ne nécessitent pas de récupération externe :
|
||||
|
||||
- Certificats auto-signés
|
||||
- Certaines AC commerciales qui regroupent les certificats intermédiaires
|
||||
- Certificats que vous avez assemblés manuellement avec la chaîne complète
|
||||
@@ -0,0 +1,622 @@
|
||||
# Déployer avec Docker / Podman
|
||||
|
||||
Le moyen le plus simple d'auto-héberger BentoPDF en production.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **En-têtes requis pour la conversion de fichiers Office**
|
||||
>
|
||||
> Les outils basés sur LibreOffice (conversion Word, Excel, PowerPoint) nécessitent ces en-têtes HTTP pour la prise en charge de `SharedArrayBuffer` :
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> La page doit aussi être servie depuis un contexte sécurisé. `http://localhost` fonctionne pour les tests locaux, mais `http://192.168.x.x` ou d'autres adresses IP du réseau local n'en font généralement pas partie : la conversion Office en HTTP simple échouera donc, même si les en-têtes sont présents.
|
||||
>
|
||||
> Les images de conteneur officielles incluent ces en-têtes. Si vous utilisez un reverse proxy (Traefik, Caddy, etc.), assurez-vous que ces en-têtes sont conservés ou ajoutés, et utilisez HTTPS pour tout accès hors bouclage local.
|
||||
|
||||
> [!TIP]
|
||||
> **Utilisateurs de Podman :** toutes les commandes `docker` fonctionnent avec Podman en remplaçant `docker` par `podman` et `docker-compose` par `podman-compose`.
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF est proposé en deux versions :
|
||||
>
|
||||
> - **Version auto-hébergée** : `ghcr.io/alam00000/bentopdf-simple:latest`. Tous les outils PDF, **sans** l'habillage marketing (ni section d'en-tête, ni FAQ, ni témoignages, ni pied de page). À utiliser pour les déploiements internes, d'équipe ou d'organisation. Ce n'est **pas** une version « allégée » aux fonctionnalités réduites.
|
||||
> - **Version commerciale** : `ghcr.io/alam00000/bentopdf:latest`. Le site marketing complet, utilisé par bentopdf.com lui-même et par les détenteurs d'une licence commerciale qui exploitent des déploiements publics.
|
||||
>
|
||||
> En cas de doute : récupérez la version auto-hébergée.
|
||||
|
||||
```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
|
||||
|
||||
Créez `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
|
||||
```
|
||||
|
||||
Exécutez :
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Construire votre propre 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;"]
|
||||
```
|
||||
|
||||
Construisez et exécutez :
|
||||
|
||||
```bash
|
||||
docker build -t bentopdf:custom .
|
||||
docker run -d -p 3000:8080 bentopdf:custom
|
||||
```
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
| Variable | Description | Par défaut |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Compile la variante auto-hébergée (masque l'habillage marketing de bentopdf.com : section d'en-tête, FAQ, témoignages, pied de page). Tous les outils PDF restent disponibles. | `false` |
|
||||
| `BASE_URL` | Déployer dans un sous-répertoire | `/` |
|
||||
| `VITE_WASM_PYMUPDF_URL` | URL du module WASM PyMuPDF | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/` |
|
||||
| `VITE_WASM_GS_URL` | URL du module WASM Ghostscript | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/` |
|
||||
| `VITE_WASM_CPDF_URL` | URL du module WASM CoherentPDF | `https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/` |
|
||||
| `VITE_TESSERACT_WORKER_URL` | URL du script worker OCR | _(vide ; CDN par défaut de Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_CORE_URL` | Dossier du runtime principal OCR | _(vide ; CDN par défaut de Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_LANG_URL` | Dossier des fichiers traineddata OCR | _(vide ; CDN par défaut de Tesseract.js)_ |
|
||||
| `VITE_TESSERACT_AVAILABLE_LANGUAGES` | Langues OCR séparées par des virgules affichées dans l'interface | _(vide ; catalogue complet affiché)_ |
|
||||
| `VITE_OCR_FONT_BASE_URL` | Dossier des polices de la couche de texte OCR | _(vide ; URL de polices Noto distantes)_ |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Langue par défaut de l'interface | `en` |
|
||||
| `VITE_BRAND_NAME` | Nom de marque personnalisé | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Chemin du logo relatif à `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Texte de pied de page/copyright personnalisé | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | ID d'outils séparés par des virgules à masquer | _(vide ; tous les outils activés)_ |
|
||||
| `DISABLE_GITHUB_STARS` | Ignore la récupération du nombre d'étoiles GitHub au chargement de la page et retire `api.github.com` de la CSP. Les builds en Simple Mode l'ignorent toujours. | `false` |
|
||||
| `SITE_URL` | URL publique écrite dans les liens canonical, hreflang et sitemap. Laissez la valeur par défaut, sauf si vous voulez que les moteurs de recherche traitent votre déploiement comme un site à part entière. | `https://www.bentopdf.com` |
|
||||
| `ALLOW_INDEXING` | Avec un `SITE_URL` personnalisé, chaque page est marquée `noindex` pour que votre copie ne concurrence pas bentopdf.com dans les résultats de recherche. Définissez `true` pour autoriser l'indexation par les moteurs de recherche. | `false` |
|
||||
|
||||
Les URL des modules WASM sont préconfigurées avec des valeurs CDN par défaut : toutes les fonctionnalités avancées fonctionnent sans configuration. Remplacez-les pour les déploiements isolés du réseau ou auto-hébergés.
|
||||
|
||||
### Moteurs de recherche
|
||||
|
||||
Par défaut, chaque page indique aux moteurs de recherche que bentopdf.com est l'original, afin que votre déploiement n'apparaisse pas comme un doublon. Vous n'avez rien à changer pour une instance privée ou interne.
|
||||
|
||||
- **Exclure totalement votre instance des résultats de recherche :** lancez le conteneur avec `-e ROBOTS_NOINDEX=true`. Un simple redémarrage suffit, sans recompilation.
|
||||
- **Faire de votre instance un site à part entière dans les résultats de recherche :** compilez avec `--build-arg SITE_URL=https://your-domain.example --build-arg ALLOW_INDEXING=true`. Sans `ALLOW_INDEXING=true`, un `SITE_URL` personnalisé marque chaque page `noindex`.
|
||||
|
||||
### Content-Security-Policy
|
||||
|
||||
L'image nginx est livrée avec un en-tête `Content-Security-Policy` appliqué. Les directives `script-src`, `connect-src` et `font-src` de la CSP sont **générées à la compilation** à partir des variables d'URL `VITE_*` ci-dessus : tous les hôtes que vous passez via `--build-arg` sont automatiquement ajoutés à la politique.
|
||||
|
||||
En conséquence :
|
||||
|
||||
- Si vous remplacez `VITE_CORS_PROXY_URL` ou `VITE_WASM_*_URL` à la compilation, la CSP autorise automatiquement ces origines, sans configuration supplémentaire.
|
||||
- Pour les URL sur `cdn.jsdelivr.net`, `script-src` n'autorise que le dossier exact du paquet versionné, par exemple `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/`, et non tout le CDN. jsDelivr sert n'importe quel paquet npm public ou dépôt GitHub : autoriser tout l'hôte permettrait à chacun de s'exécuter sur votre instance. Les URL sur tout autre hôte sont autorisées par origine.
|
||||
- Si vous configurez des URL WASM personnalisées _à l'exécution_ via la page Paramètres avancés de l'application, ces origines ne figurent **pas** dans la CSP et le navigateur bloquera les requêtes vers elles. Cela s'applique aussi à une autre version du même paquet sur `cdn.jsdelivr.net`, car seule la version définie à la compilation est autorisée. La configuration à l'exécution est prévue pour l'expérimentation ; pour des URL personnalisées permanentes, définissez l'argument de build `VITE_*` correspondant.
|
||||
- Dans les déploiements isolés du réseau qui remplacent les trois valeurs `VITE_WASM_*_URL`, ces dossiers de paquets `cdn.jsdelivr.net` sont aussi retirés de la CSP (chaque valeur par défaut est remplacée, pas complétée). Les valeurs par défaut de Tesseract OCR sur `cdn.jsdelivr.net` restent dans la politique tant que vous ne définissez pas aussi les trois valeurs `VITE_TESSERACT_*`. De même, définir `VITE_CORS_PROXY_URL` remplace la valeur publique par défaut `bentopdf-cors-proxy.bentopdf.workers.dev`.
|
||||
|
||||
La CSP inclut `'unsafe-eval'` dans `script-src`, car le runtime LibreOffice WASM (utilisé par les outils de conversion Word/Excel/PowerPoint) et la bibliothèque d'images wasm-vips (utilisée par PDF vers TIFF) compilent du code de dispatch interne via `new Function()`. Le supprimer casserait ces outils. Si vous compilez en `SIMPLE_MODE` (sans LibreOffice) et n'avez pas besoin de PDF vers TIFF, vous pouvez modifier manuellement le fichier `security-headers.conf` généré pour retirer `'unsafe-eval'` et obtenir une politique plus stricte.
|
||||
|
||||
Pour l'OCR, laissez les variables `VITE_TESSERACT_*` vides afin d'utiliser les ressources en ligne par défaut, ou définissez les trois ensemble pour un OCR auto-hébergé/hors ligne. Les remplacements OCR partiels sont refusés, car le worker, le runtime principal et le dossier traineddata doivent correspondre. Pour une production de PDF avec texte recherchable entièrement hors ligne, définissez aussi `VITE_OCR_FONT_BASE_URL` afin que les polices de la couche de texte OCR soient chargées depuis votre serveur interne plutôt que depuis les URL publiques des polices Noto.
|
||||
|
||||
`VITE_DEFAULT_LANGUAGE` définit la langue de l'interface pour les nouveaux visiteurs. Valeurs prises en charge : `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Les utilisateurs peuvent toujours changer de langue : cela ne modifie que la valeur par défaut.
|
||||
|
||||
Exemple :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Image de marque personnalisée
|
||||
|
||||
Remplacez le logo, le nom et le texte de pied de page par défaut de BentoPDF par les vôtres. Placez votre fichier de logo dans le dossier `public/` (ou utilisez une image existante), puis passez les variables de marque à la compilation :
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
La personnalisation fonctionne en mode complet comme en Simple Mode, et peut se combiner avec toutes les autres options de compilation.
|
||||
|
||||
### Désactiver des outils spécifiques
|
||||
|
||||
Masquez des outils de l'interface pour des exigences de conformité ou de sécurité. Les outils désactivés sont retirés de la page d'accueil, des résultats de recherche, des raccourcis clavier et du créateur de workflows. L'accès direct par URL affiche une page « outil indisponible ».
|
||||
|
||||
Les ID d'outils correspondent à l'URL de la page sans `.html`. Par exemple, si l'outil se trouve à `bentopdf.com/edit-pdf.html`, l'ID est `edit-pdf`.
|
||||
|
||||
#### Trouver les ID d'outils
|
||||
|
||||
Le plus simple : ouvrez n'importe quel outil dans BentoPDF et regardez l'URL. La dernière partie du chemin (sans `.html`) est l'ID de l'outil.
|
||||
|
||||
<details>
|
||||
<summary>Liste complète des ID d'outils</summary>
|
||||
|
||||
**Éditer et annoter :** `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`
|
||||
|
||||
**Convertir en 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`
|
||||
|
||||
**Convertir depuis 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 et gérer :** `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`
|
||||
|
||||
**Optimiser et réparer :** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Sécurité :** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Option 1 : à la compilation (argument de build Docker)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
Cela intègre la liste des outils désactivés dans le bundle JavaScript. Une recompilation est nécessaire pour la modifier.
|
||||
|
||||
#### Option 2 : à l'exécution (config.json)
|
||||
|
||||
Montez un fichier `config.json` dans le répertoire servi, sans recompilation :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Ou avec 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
|
||||
```
|
||||
|
||||
Les deux méthodes peuvent être combinées : les listes sont fusionnées. Si un outil figure dans l'une ou l'autre, il est désactivé.
|
||||
|
||||
#### Désactiver des fonctionnalités de l'éditeur
|
||||
|
||||
Vous pouvez aussi désactiver des fonctionnalités précises de l'éditeur PDF (p. ex. caviardage, annotations, formulaires) sans désactiver l'outil d'édition entier. Ajoutez `editorDisabledCategories` à votre `config.json` :
|
||||
|
||||
```json
|
||||
{
|
||||
"editorDisabledCategories": ["redaction", "annotation-stamp"]
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Liste complète des catégories de l'éditeur</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`
|
||||
|
||||
**Formes :** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
|
||||
|
||||
**Formulaire :** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
|
||||
|
||||
**Caviardage :** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
|
||||
|
||||
**Document :** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
|
||||
|
||||
**Page :** `page`, `spread`, `rotate`, `scroll`, `navigation`
|
||||
|
||||
**Panneau :** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
|
||||
|
||||
**Outils :** `tools`, `pan`, `pointer`, `capture`
|
||||
|
||||
**Sélection :** `selection`, `selection-copy`
|
||||
|
||||
**Historique :** `history`, `history-undo`, `history-redo`
|
||||
|
||||
</details>
|
||||
|
||||
Les catégories sont hiérarchiques : désactiver un parent (p. ex. `annotation`) désactive tous ses enfants.
|
||||
|
||||
### Désactiver le compteur d'étoiles GitHub
|
||||
|
||||
La barre de navigation affiche le nombre d'étoiles GitHub en direct, récupéré depuis `api.github.com` au chargement de la page. C'est la seule requête externe qui ne sert pas une fonctionnalité PDF. Désactivez-la à la compilation pour retirer le code de récupération du bundle et supprimer `api.github.com` de la Content-Security-Policy :
|
||||
|
||||
```bash
|
||||
docker build --build-arg DISABLE_GITHUB_STARS=true -t bentopdf .
|
||||
```
|
||||
|
||||
Les builds en Simple Mode (`bentopdf-simple`) ignorent toujours le compteur d'étoiles : cette option n'est utile que pour la version commerciale ou les builds complets personnalisés.
|
||||
|
||||
### URL WASM personnalisées (isolé du réseau / auto-hébergé)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Les URL WASM sont intégrées au JavaScript **à la compilation**. Les fichiers WASM sont téléchargés par le **navigateur de l'utilisateur** à l'exécution : Docker ne les télécharge pas pendant le build. Pour les réseaux isolés, vous devez héberger les fichiers WASM sur un serveur interne accessible aux navigateurs.
|
||||
|
||||
**Workflow complet pour un réseau isolé :**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Utilisez pour `--ocr-languages` les codes affichés par `bash scripts/prepare-airgap.sh --list-ocr-languages`, ou cherchez par nom avec `bash scripts/prepare-airgap.sh --search-ocr-language <term>`. Lorsque vous compilez avec un sous-ensemble OCR restreint, passez les mêmes codes à `VITE_TESSERACT_AVAILABLE_LANGUAGES` afin que l'application n'affiche que les langues incluses. Pour une sortie OCR entièrement hors ligne, hébergez aussi le dossier `ocr-fonts/` fourni et pointez `VITE_OCR_FONT_BASE_URL` vers lui.
|
||||
|
||||
Définissez une variable sur une chaîne vide pour désactiver ce module (les utilisateurs devront le configurer manuellement via les Paramètres avancés).
|
||||
|
||||
## Port personnalisé
|
||||
|
||||
Par défaut, BentoPDF écoute sur le port `8080` à l'intérieur du conteneur. Pour le modifier, définissez la variable d'environnement `PORT` à l'exécution :
|
||||
|
||||
```bash
|
||||
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
| Variable | Description | Par défaut |
|
||||
| -------- | ------------------------------------- | ---------- |
|
||||
| `PORT` | Port d'écoute Nginx dans le conteneur | `8080` |
|
||||
|
||||
Cela fonctionne avec le Dockerfile standard comme avec le Dockerfile nonroot.
|
||||
|
||||
## ID utilisateur personnalisé (PUID/PGID)
|
||||
|
||||
Pour les environnements qui exigent une exécution sous un utilisateur non root précis (NAS, Kubernetes avec contextes de sécurité, politiques d'organisation), BentoPDF fournit un Dockerfile distinct avec prise en charge PUID/PGID de style LSIO.
|
||||
|
||||
### Construire et exécuter
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
| Variable | Description | Par défaut |
|
||||
| -------------- | -------------------------- | ---------- |
|
||||
| `PUID` | ID utilisateur d'exécution | `1000` |
|
||||
| `PGID` | ID de groupe d'exécution | `1000` |
|
||||
| `PORT` | Port d'écoute Nginx | `8080` |
|
||||
| `DISABLE_IPV6` | Désactiver l'écoute IPv6 | `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
|
||||
```
|
||||
|
||||
### Fonctionnement
|
||||
|
||||
Le conteneur démarre en root, crée un utilisateur avec le PUID/PGID indiqué, ajuste la propriété de tous les répertoires inscriptibles, puis abandonne ses privilèges avec `su-exec`. Le processus nginx s'exécute entièrement sous l'utilisateur que vous avez indiqué.
|
||||
|
||||
> [!NOTE]
|
||||
> Le `Dockerfile` standard utilise `nginx-unprivileged` (UID 101) et est recommandé pour la plupart des déploiements. N'utilisez `Dockerfile.nonroot` que si vous avez besoin d'un UID/GID précis.
|
||||
|
||||
> [!WARNING]
|
||||
> PUID/PGID ne peuvent pas valoir `0` (root). Le point d'entrée valide les valeurs et s'arrête avec une erreur si elles sont invalides.
|
||||
|
||||
## Avec 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
|
||||
```
|
||||
|
||||
## Avec 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"
|
||||
}
|
||||
```
|
||||
|
||||
## Limites de ressources
|
||||
|
||||
```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 (intégration systemd)
|
||||
|
||||
[Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) permet d'exécuter des conteneurs Podman comme services systemd. C'est idéal pour les déploiements en production sur Linux.
|
||||
|
||||
### Configuration Quadlet de base
|
||||
|
||||
Créez un fichier d'unité de conteneur à `~/.config/containers/systemd/bentopdf.container` (utilisateur) ou `/etc/containers/systemd/bentopdf.container` (système) :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Activer et démarrer
|
||||
|
||||
```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]
|
||||
> Pour un déploiement à l'échelle du système, utilisez `systemctl` sans l'option `--user` et placez le fichier dans `/etc/containers/systemd/`.
|
||||
|
||||
### Quadlet avec contrôle de santé
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Mise à jour automatique avec Quadlet
|
||||
|
||||
Podman peut mettre à jour automatiquement les conteneurs lorsque de nouvelles images sont disponibles :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Configuration réseau Quadlet
|
||||
|
||||
Pour une configuration réseau personnalisée, créez un fichier réseau `bentopdf.network` :
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Subnet=10.89.0.0/24
|
||||
Gateway=10.89.0.1
|
||||
```
|
||||
|
||||
Référencez-le ensuite dans votre fichier de conteneur :
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
Network=bentopdf.network
|
||||
```
|
||||
|
||||
## Mise à jour
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Recreate container
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Déployer sur Hostinger
|
||||
|
||||
[Hostinger](https://hostinger.com) est un hébergeur mutualisé très répandu. Ce guide explique comment déployer BentoPDF sur l'hébergement mutualisé de Hostinger.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Offre d'hébergement Hostinger avec accès au gestionnaire de fichiers
|
||||
- Node.js installé en local pour la compilation
|
||||
|
||||
## Étape 1 : compiler le projet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Les fichiers compilés se trouvent dans le dossier `dist`.
|
||||
|
||||
## Étape 2 : téléverser sur Hostinger
|
||||
|
||||
### Déploiement à la racine du domaine
|
||||
|
||||
1. Connectez-vous à votre compte Hostinger
|
||||
2. Allez dans **File Manager** → **public_html**
|
||||
3. **Supprimez** tous les fichiers existants (faites une sauvegarde si nécessaire)
|
||||
4. **Téléversez** tout le contenu de votre dossier local `dist` dans `public_html`
|
||||
|
||||
### Déploiement dans un sous-répertoire
|
||||
|
||||
Si vous déployez dans un sous-répertoire (p. ex. `yourdomain.com/pdf-tools/`) :
|
||||
|
||||
1. Compilez avec la bonne URL de base :
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
2. Créez le dossier dans Hostinger :
|
||||
- Allez dans **File Manager** → **public_html**
|
||||
- Créez un nouveau dossier : `pdf-tools`
|
||||
3. Téléversez tout le contenu de `dist` dans `public_html/pdf-tools/`
|
||||
|
||||
## Étape 3 : créer le fichier .htaccess
|
||||
|
||||
Créez un fichier `.htaccess` à la racine de votre dossier de déploiement (`public_html` ou `public_html/pdf-tools/`) :
|
||||
|
||||
::: warning Important
|
||||
Pour un déploiement dans un sous-répertoire, remplacez `RewriteBase /` par `RewriteBase /pdf-tools/` (ou le nom de votre dossier).
|
||||
:::
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Exemple de .htaccess pour un sous-répertoire
|
||||
|
||||
Pour `yourdomain.com/pdf-tools/`, modifiez ces lignes :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Les fichiers WASM ne se chargent pas
|
||||
|
||||
Vérifiez que les types MIME sont correctement définis dans `.htaccess` :
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Les outils LibreOffice ne fonctionnent pas
|
||||
|
||||
Les en-têtes de sécurité sont essentiels pour SharedArrayBuffer :
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
```
|
||||
|
||||
Si les en-têtes ne sont pas appliqués, contactez le support Hostinger pour activer `mod_headers`.
|
||||
|
||||
### Erreurs 404 lors de l'actualisation de la page
|
||||
|
||||
Assurez-vous que la règle d'extension `.html` et les routes de langue sont correctement configurées. BentoPDF utilise des fichiers HTML statiques, pas un routage SPA :
|
||||
|
||||
```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]
|
||||
```
|
||||
|
||||
### Limites de téléversement
|
||||
|
||||
Hostinger peut imposer des limites de taille de fichier. Téléversez les fichiers WASM séparément si le téléversement groupé échoue.
|
||||
@@ -0,0 +1,538 @@
|
||||
# Guide d'auto-hébergement
|
||||
|
||||
BentoPDF peut être auto-hébergé sur votre propre infrastructure. Ce guide présente différentes options de déploiement.
|
||||
|
||||
## Démarrage rapide avec Docker / Podman
|
||||
|
||||
Le moyen le plus rapide d'auto-héberger BentoPDF :
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF est disponible en deux versions :
|
||||
>
|
||||
> - **Version auto-hébergée** : `ghcr.io/alam00000/bentopdf-simple:latest`. Tous les outils PDF, **sans** l'habillage marketing (pas de section d'accueil, de FAQ, de témoignages ni de pied de page). À utiliser pour les déploiements internes, d'équipe ou d'organisation. Ce **n'est pas** une version « allégée » avec moins de fonctionnalités.
|
||||
> - **Version commerciale** : `ghcr.io/alam00000/bentopdf:latest`. Le site marketing complet, utilisé par bentopdf.com lui-même et par les titulaires d'une licence commerciale qui exploitent des déploiements publics.
|
||||
>
|
||||
> Dans le doute : utilisez la version auto-hébergée.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> La conversion de fichiers Office nécessite `SharedArrayBuffer`, ce qui impose que l'application soit à la fois isolée cross-origin et servie depuis un contexte sécurisé. L'image officielle envoie déjà les en-têtes COOP/COEP requis, mais les navigateurs désactivent quand même `SharedArrayBuffer` sur les origines HTTP simples du réseau local comme `http://192.168.x.x`.
|
||||
>
|
||||
> N'utilisez `http://localhost` que pour des tests sur le même appareil. Si les utilisateurs accèdent à BentoPDF via une IP ou un nom d'hôte du réseau local, terminez la connexion en 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
|
||||
```
|
||||
|
||||
Ou avec 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 (systemd sous Linux)
|
||||
|
||||
Exécutez BentoPDF comme service systemd. Créez `~/.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
|
||||
```
|
||||
|
||||
Consultez le [guide de déploiement Docker](/fr/self-hosting/docker) pour la documentation complète de Quadlet.
|
||||
|
||||
## Compiler depuis les sources
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Options de configuration
|
||||
|
||||
### Version auto-hébergée (mode simple)
|
||||
|
||||
La version auto-hébergée (l'image `bentopdf-simple`, aussi appelée mode simple) est **fonctionnellement identique** à la version commerciale : tous les outils PDF sont présents et se comportent de la même façon. Elle masque simplement l'habillage marketing qui n'a de sens que sur bentopdf.com ou sur un déploiement commercial public. **Ce n'est pas une version allégée avec moins de fonctionnalités.**
|
||||
|
||||
**Ce que la version auto-hébergée masque** (purement cosmétique, aucune fonctionnalité PDF n'est retirée) :
|
||||
|
||||
- Barre de navigation, section d'accueil, section des fonctionnalités, FAQ, témoignages, pied de page
|
||||
- Remplace le titre de la page par « Outils PDF »
|
||||
|
||||
**Ce que la version auto-hébergée conserve** (tout ce qui traite réellement les PDF) :
|
||||
|
||||
- Tous les outils PDF (fusion, scission, édition, signature, OCR, conversion Office, etc.)
|
||||
- La personnalisation de la marque et toute la configuration au build et à l'exécution
|
||||
|
||||
La version commerciale (`ghcr.io/alam00000/bentopdf:latest`) est celle qui alimente bentopdf.com et qu'utilisent les titulaires d'une licence commerciale pour des déploiements publics : elle ajoute la section d'accueil, la FAQ, les témoignages et le pied de page qui n'auraient pas de sens sur un outil interne.
|
||||
|
||||
Si vous auto-hébergez BentoPDF pour votre équipe, votre organisation ou comme outil interne, utilisez la version auto-hébergée :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Consultez [SIMPLE_MODE.md](https://github.com/alam00000/bentopdf/blob/main/SIMPLE_MODE.md) pour tous les détails.
|
||||
|
||||
### Version commerciale (déploiements publics sous votre propre marque)
|
||||
|
||||
La version commerciale (l'image `bentopdf`, sans suffixe `-simple`) est celle qui alimente bentopdf.com. Elle inclut le site marketing complet (section d'accueil, fonctionnalités, FAQ, témoignages, pied de page) en plus de tous les outils PDF. Utilisez cette version si vous souhaitez **déployer BentoPDF comme service PDF public sous votre propre marque**, par exemple :
|
||||
|
||||
- Vous exploitez BentoPDF comme service SaaS hébergé pour des utilisateurs finaux sur votre propre domaine
|
||||
- Vous voulez l'expérience de la page d'accueil (sections marketing + outils), et pas seulement la surface brute des outils
|
||||
- Vous êtes titulaire d'une licence commerciale et intégrez BentoPDF dans un produit commercial
|
||||
|
||||
**Exécutez-la telle quelle** (marque BentoPDF, utile pour prévisualiser la version) :
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
**Compilez-la avec votre propre marque** (le flux commercial typique) :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Toutes les autres options de build (`BASE_URL`, `VITE_DEFAULT_LANGUAGE`, `DISABLE_TOOLS`, remplacements d'URL WASM, etc.) fonctionnent comme sur la version auto-hébergée.
|
||||
|
||||
::: warning Licence
|
||||
L'exécution de la version commerciale est autorisée selon l'une des deux options de licence de BentoPDF :
|
||||
|
||||
- **AGPL-3.0** (gratuite) : autorisée si votre déploiement publie l'intégralité de son code source sous AGPL, y compris les modifications de marque et la logique métier associée.
|
||||
- **Licence commerciale** (79 $, à vie) : requise pour les déploiements propriétaires ou à code fermé où vous ne publiez pas votre fork de marque ni votre code métier.
|
||||
|
||||
Consultez la [page Licence](https://www.bentopdf.com/licensing) pour la comparaison complète. Les modules WASM sous licence AGPL (PyMuPDF, Ghostscript, CoherentPDF) sont chargés depuis un CDN à l'exécution : ils n'entrent donc pas dans votre image et ne changent pas votre situation en matière de licence.
|
||||
:::
|
||||
|
||||
### URL de base
|
||||
|
||||
Déployer dans un sous-répertoire :
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
### Marque personnalisée
|
||||
|
||||
Remplacez le logo, le nom et le texte de pied de page par défaut de BentoPDF par les vôtres au moment du build :
|
||||
|
||||
| Variable | Description | Valeur par défaut |
|
||||
| ------------------ | ------------------------------------------------------- | --------------------------------------- |
|
||||
| `VITE_BRAND_NAME` | Nom de marque affiché dans l'en-tête et le pied de page | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Chemin du logo relatif à `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Texte personnalisé du pied de page/copyright | `© 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
|
||||
```
|
||||
|
||||
Ou 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 .
|
||||
```
|
||||
|
||||
La personnalisation de la marque fonctionne en mode complet comme en mode simple, et peut être combinée avec toutes les autres options de build (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Désactiver des outils précis
|
||||
|
||||
Masquez des outils individuels pour des raisons de conformité ou de sécurité. Les outils désactivés sont retirés de la page d'accueil, de la recherche, des raccourcis, du constructeur de workflow et de l'accès direct par URL.
|
||||
|
||||
Les identifiants d'outils correspondent à l'URL de la page sans `.html` : ouvrez un outil et regardez l'URL (par ex. `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Au build** (intégré au bundle) :
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**À l'exécution** (sans reconstruire : montez un `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
|
||||
```
|
||||
|
||||
Les deux méthodes peuvent être combinées : les listes sont fusionnées.
|
||||
|
||||
Vous pouvez aussi désactiver des fonctionnalités précises de l'éditeur PDF (par ex. la rédaction) sans désactiver tout l'outil, en ajoutant `editorDisabledCategories` à `config.json`. Consultez le [guide Docker](/fr/self-hosting/docker#desactiver-des-fonctionnalites-de-l-editeur) pour la liste complète des catégories.
|
||||
|
||||
## Guides de déploiement
|
||||
|
||||
Choisissez votre plateforme :
|
||||
|
||||
- [Vercel](/fr/self-hosting/vercel)
|
||||
- [Netlify](/fr/self-hosting/netlify)
|
||||
- [Cloudflare Pages](/fr/self-hosting/cloudflare)
|
||||
- [AWS S3 + CloudFront](/fr/self-hosting/aws)
|
||||
- [Hostinger](/fr/self-hosting/hostinger)
|
||||
- [Nginx](/fr/self-hosting/nginx)
|
||||
- [Apache](/fr/self-hosting/apache)
|
||||
- [Docker](/fr/self-hosting/docker)
|
||||
- [Kubernetes](/fr/self-hosting/kubernetes)
|
||||
- [Proxy CORS](/fr/self-hosting/cors-proxy) : requis pour les signatures numériques
|
||||
|
||||
## Problèmes courants
|
||||
|
||||
### La conversion Word / ODT / Excel / PowerPoint vers PDF se bloque (SharedArrayBuffer indisponible)
|
||||
|
||||
**Symptôme** : les conversions de documents basées sur LibreOffice (Word, ODT, Excel, PowerPoint vers PDF) se bloquent à environ 55 % ou ne démarrent pas. La console du navigateur peut afficher :
|
||||
|
||||
> `ReferenceError: SharedArrayBuffer is not defined`
|
||||
|
||||
…ou `window.crossOriginIsolated` renvoie `false`, ou la compilation WASM signale `expected magic word 00 61 73 6d, found 1f 8b 08 08`.
|
||||
|
||||
**Cause** : LibreOffice WASM nécessite `SharedArrayBuffer`, que le navigateur n'active que si la page est **isolée cross-origin** ET servie depuis un **contexte sécurisé**. Deux conditions doivent donc être remplies :
|
||||
|
||||
1. Chaque réponse inclut les deux en-têtes :
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
2. La page est servie depuis `https://...` ou `http://localhost`. Le HTTP simple sur une IP du réseau local (par ex. `http://192.168.x.x`) n'est PAS considéré comme sécurisé : les navigateurs y désactivent `SharedArrayBuffer`.
|
||||
|
||||
L'écart `00 61 73 6d / 1f 8b 08 08` est un sous-symptôme distinct : il manque l'en-tête de réponse `Content-Encoding: gzip` sur les fichiers précompressés `.wasm.gz` / `.data.gz`, si bien que le navigateur reçoit des octets gzip bruts au lieu du WASM décompressé.
|
||||
|
||||
**Solution** : consultez le guide de déploiement de votre plateforme pour la configuration exacte :
|
||||
|
||||
- [Nginx →](/fr/self-hosting/nginx#la-conversion-word-odt-excel-vers-pdf-ne-fonctionne-pas)
|
||||
- [Apache →](/fr/self-hosting/apache#la-conversion-word-odt-excel-vers-pdf-ne-fonctionne-pas)
|
||||
- [AWS S3 + CloudFront →](/fr/self-hosting/aws#etape-3b-politique-d-en-tetes-de-reponse-requise-pour-libreoffice-wasm)
|
||||
- [Cloudflare Pages →](/fr/self-hosting/cloudflare#fichier-de-configuration) (`_headers` file)
|
||||
- [Netlify →](/fr/self-hosting/netlify#la-conversion-word-odt-excel-vers-pdf-reste-bloquee-a-55) (`netlify.toml`)
|
||||
- [Vercel →](/fr/self-hosting/vercel#la-conversion-word-odt-excel-vers-pdf-ne-fonctionne-pas) (`vercel.json`)
|
||||
- [Hostinger →](/fr/self-hosting/hostinger#les-outils-libreoffice-ne-fonctionnent-pas) (`.htaccess`)
|
||||
- [Kubernetes →](/fr/self-hosting/kubernetes#verifier-que-les-en-tetes-sharedarraybuffer-fonctionnent-toujours-ingress-gateway)
|
||||
- **Docker** : géré automatiquement par la configuration nginx fournie, aucune action nécessaire.
|
||||
|
||||
**Vérification** : ouvrez la console des DevTools sur n'importe quelle page BentoPDF et exécutez :
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Si la page est en HTTPS ou en `http://localhost` ET que les deux en-têtes COEP/COOP sont présents sur chaque réponse, les deux vérifications passent. Si vous êtes sur `http://192.168.x.x` ou une autre origine HTTP hors boucle locale, terminez la connexion en HTTPS : aucune correction par simples en-têtes n'existe.
|
||||
|
||||
### Fichiers `.mjs` servis en `application/octet-stream`
|
||||
|
||||
**Symptôme** : Signer un PDF, le remplissage de formulaire et certains autres outils affichent un lecteur vide ou ne se chargent pas. La console du navigateur signale :
|
||||
|
||||
> `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.`
|
||||
|
||||
**Cause** : votre serveur web ou reverse proxy n'a pas de correspondance de type MIME pour les fichiers `.mjs` (le lecteur PDF intégré fournit des modules ES avec cette extension). De nombreuses configurations de serveur par défaut utilisent `application/octet-stream` pour les extensions inconnues, ce que les navigateurs refusent d'exécuter comme modules ES.
|
||||
|
||||
**Solution** : consultez le guide de déploiement de votre plateforme pour l'extrait exact :
|
||||
|
||||
- [Nginx →](/fr/self-hosting/nginx#signer-un-pdf-ou-le-remplissage-de-formulaire-affiche-un-lecteur-vide-erreur-mime-mjs)
|
||||
- [Apache →](/fr/self-hosting/apache#signer-un-pdf-ou-le-remplissage-de-formulaires-affiche-un-visualiseur-vide-erreur-mime-mjs)
|
||||
- [AWS S3 + CloudFront →](/fr/self-hosting/aws#etape-2-compiler-et-televerser) (voir `aws s3 cp ... --include "*.mjs"`)
|
||||
- [Kubernetes →](/fr/self-hosting/kubernetes#erreurs-de-type-mime-mjs-iframe-vide-dans-signer-un-pdf-remplissage-de-formulaire)
|
||||
- **Docker, Vercel, Netlify, Cloudflare Pages, Hostinger** : géré automatiquement, aucune action nécessaire.
|
||||
|
||||
**Vérification** : ouvrez les DevTools, onglet Network, repérez la requête `.mjs` en échec et vérifiez l'en-tête de réponse `Content-Type`. Il doit valoir `application/javascript`. S'il vaut toujours `application/octet-stream`, un reverse proxy ou un CDN en amont détecte peut-être à nouveau le type : vérifiez chaque couche de votre chaîne de diffusion.
|
||||
|
||||
## Configuration WASM (composants AGPL)
|
||||
|
||||
BentoPDF **n'intègre pas** de bibliothèques de traitement sous licence AGPL dans son code source, mais **préconfigure des URL de CDN** pour que toutes les fonctionnalités marchent immédiatement, sans configuration manuelle.
|
||||
|
||||
::: tip Zéro configuration par défaut
|
||||
Depuis la v2.0.0, les modules WASM sont préconfigurés pour se charger depuis le CDN jsDelivr via des variables d'environnement. Toutes les fonctionnalités avancées fonctionnent immédiatement, sans aucune configuration de l'utilisateur.
|
||||
:::
|
||||
|
||||
| Composant | Licence | Fonctionnalités |
|
||||
| --------------- | -------- | ------------------------------------------------------------------------- |
|
||||
| **PyMuPDF** | AGPL-3.0 | Conversion EPUB/MOBI/FB2/XPS, extraction d'images, extraction de tableaux |
|
||||
| **Ghostscript** | AGPL-3.0 | Conversion PDF/A, compression, redressement, rastérisation |
|
||||
| **CoherentPDF** | AGPL-3.0 | Table des matières, pièces jointes, fusion de PDF avec signets |
|
||||
|
||||
### Variables d'environnement par défaut
|
||||
|
||||
Elles sont définies dans `.env.production` et intégrées au build :
|
||||
|
||||
```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=
|
||||
```
|
||||
|
||||
### Remplacer les URL WASM
|
||||
|
||||
Vous pouvez remplacer les valeurs par défaut au moment du build pour des déploiements personnalisés :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Pour désactiver complètement un module (la configuration manuelle par l'utilisateur via les paramètres avancés devient alors nécessaire), définissez sa variable sur une chaîne vide.
|
||||
|
||||
Pour l'OCR, laissez toutes les variables `VITE_TESSERACT_*` vides et conservez les ressources en ligne par défaut, ou définissez ensemble les URL worker/core/lang pour un OCR auto-hébergé ou hors ligne. Si vous n'intégrez que certaines langues d'OCR, définissez aussi `VITE_TESSERACT_AVAILABLE_LANGUAGES` avec les mêmes codes séparés par des virgules afin que l'interface ne propose que les langues installées et que les sélections non prises en charge échouent avec une erreur explicite. Pour une sortie PDF consultable entièrement hors ligne, définissez aussi `VITE_OCR_FONT_BASE_URL` sur le répertoire interne qui sert les polices OCR fournies.
|
||||
|
||||
Les utilisateurs peuvent aussi remplacer ces valeurs à tout moment via les **paramètres avancés** de l'interface : les remplacements enregistrés dans le navigateur ont priorité sur les valeurs d'environnement par défaut.
|
||||
|
||||
### Déploiement isolé du réseau / hors ligne
|
||||
|
||||
Pour les réseaux sans accès à Internet (administrations, santé, finance, etc.). Les URL WASM sont intégrées au JavaScript **au moment du build** : les fichiers WASM eux-mêmes sont téléchargés par le **navigateur de l'utilisateur** à l'exécution. Vous devez donc tout préparer sur une machine connectée à Internet, puis transférer le résultat dans le réseau isolé.
|
||||
|
||||
#### Script automatisé (recommandé)
|
||||
|
||||
Le script `prepare-airgap.sh` fourni automatise tout le processus : téléchargement des paquets WASM, construction de l'image Docker et production d'un bundle autonome avec un script d'installation.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Cela produit un répertoire de bundle :
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Transférez le bundle dans le réseau isolé par clé USB, dépôt d'artefacts interne ou toute méthode approuvée. Exécutez ensuite le script d'installation fourni :
|
||||
|
||||
```bash
|
||||
cd bentopdf-airgap-bundle
|
||||
bash setup.sh
|
||||
```
|
||||
|
||||
Le script d'installation charge l'image Docker, extrait les fichiers WASM et démarre éventuellement le conteneur.
|
||||
|
||||
**Options du script :**
|
||||
|
||||
| Option | Description | Valeur par défaut |
|
||||
| ------------------------------ | ------------------------------------------------------------- | ------------------------------------ |
|
||||
| `--wasm-base-url <url>` | Emplacement où les WASM seront hébergés en interne | _(obligatoire, demandée si absente)_ |
|
||||
| `--image-name <name>` | Tag de l'image Docker | `bentopdf` |
|
||||
| `--output-dir <path>` | Répertoire de sortie du bundle | `./bentopdf-airgap-bundle` |
|
||||
| `--simple-mode` | Active le mode simple | désactivé |
|
||||
| `--base-url <path>` | URL de base du sous-répertoire (par ex. `/pdf/`) | `/` |
|
||||
| `--language <code>` | Langue par défaut de l'interface (par ex. `fr`, `de`) | _(aucune)_ |
|
||||
| `--brand-name <name>` | Nom de marque personnalisé | _(aucun)_ |
|
||||
| `--brand-logo <path>` | Chemin du logo relatif à `public/` | _(aucun)_ |
|
||||
| `--footer-text <text>` | Texte de pied de page personnalisé | _(aucun)_ |
|
||||
| `--ocr-languages <list>` | Langues d'OCR à intégrer, séparées par des virgules | `eng` |
|
||||
| `--list-ocr-languages` | Affiche les codes et noms d'OCR pris en charge, puis quitte | désactivé |
|
||||
| `--search-ocr-language <term>` | Recherche des codes d'OCR par nom ou abréviation | désactivé |
|
||||
| `--dockerfile <path>` | Dockerfile à utiliser | `Dockerfile` |
|
||||
| `--skip-docker` | Ignore la construction et l'export Docker | désactivé |
|
||||
| `--skip-wasm` | Ignore le téléchargement WASM (réutilise les fichiers `.tgz`) | désactivé |
|
||||
|
||||
L'invite interactive accepte aussi `list` pour afficher la liste complète des codes Tesseract pris en charge et `search <term>` pour trouver des correspondances, par exemple `search german` ou `search chi`.
|
||||
|
||||
::: warning Exigence de même origine
|
||||
Les fichiers WASM doivent être servis depuis la **même origine** que l'application BentoPDF. Les Web Workers utilisent `importScripts()`, qui ne peut pas charger de scripts cross-origin. Par exemple, si BentoPDF tourne sur `https://internal.example.com`, l'URL de base WASM doit aussi être `https://internal.example.com/wasm`.
|
||||
:::
|
||||
|
||||
#### Étapes manuelles
|
||||
|
||||
<details>
|
||||
<summary>Si vous préférez procéder manuellement, sans le script</summary>
|
||||
|
||||
**Étape 1 : télécharger les paquets WASM et OCR** (sur une machine connectée à 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
|
||||
```
|
||||
|
||||
**Étape 2 : construire l'image Docker avec les URL internes**
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
**Étape 3 : exporter l'image Docker**
|
||||
|
||||
```bash
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
```
|
||||
|
||||
**Étape 4 : transférer dans le réseau isolé**
|
||||
|
||||
Copiez par clé USB, dépôt d'artefacts interne ou méthode de transfert approuvée :
|
||||
|
||||
- `bentopdf.tar` : l'image Docker
|
||||
- Les cinq paquets `.tgz` WASM/OCR de l'étape 1
|
||||
- Le répertoire `tesseract-langdata/` de l'étape 1
|
||||
- Le répertoire `ocr-fonts/` de l'étape 1
|
||||
|
||||
**Étape 5 : installer dans le réseau isolé**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Vérifiez que les fichiers sont accessibles aux URL configurées à l'étape 2, notamment `.../ocr/worker.min.js`, `.../ocr/core`, `.../ocr/lang-data` et `.../ocr/fonts`.
|
||||
|
||||
</details>
|
||||
|
||||
::: info Compilation depuis les sources plutôt que Docker ?
|
||||
Définissez les variables dans `.env.production` avant d'exécuter `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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Héberger votre propre proxy WASM
|
||||
|
||||
Si vous devez servir des fichiers WASM AGPL avec les en-têtes CORS appropriés, vous pouvez déployer un proxy simple. Consultez le [guide du proxy WASM Cloudflare](https://github.com/alam00000/bentopdf/blob/main/cloudflare/WASM-PROXY.md) pour un exemple d'implémentation.
|
||||
|
||||
::: tip Pourquoi cette séparation ?
|
||||
Cette séparation garantit :
|
||||
|
||||
- Une conformité juridique claire pour les utilisateurs commerciaux
|
||||
- Le cœur de BentoPDF reste sous sa double licence (AGPL-3.0 / commerciale)
|
||||
- Les fichiers WASM sont chargés à l'exécution et non intégrés au code source
|
||||
:::
|
||||
|
||||
## Configuration requise
|
||||
|
||||
| Exigence | Minimum |
|
||||
| -------- | ------------------------------------ |
|
||||
| Stockage | ~100 Mo (cœur sans les modules AGPL) |
|
||||
| RAM | 512 Mo |
|
||||
| CPU | Tout processeur moderne |
|
||||
|
||||
::: tip
|
||||
BentoPDF est un site statique : aucune base de données ni serveur backend n'est nécessaire !
|
||||
:::
|
||||
@@ -0,0 +1,211 @@
|
||||
# Déployer avec Kubernetes
|
||||
|
||||
Kubernetes est peut-être excessif pour un site statique, mais il peut très bien convenir si vous standardisez déjà sur Helm + GitOps.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **En-têtes requis pour la conversion de fichiers Office**
|
||||
>
|
||||
> Les outils basés sur LibreOffice (conversion Word, Excel, PowerPoint) nécessitent ces en-têtes HTTP pour la prise en charge de `SharedArrayBuffer` :
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Les images nginx officielles de BentoPDF incluent ces en-têtes. Dans Kubernetes, **les contrôleurs Ingress/Gateway sont aussi des reverse proxies** : assurez-vous donc que ces en-têtes sont conservés (ou ajoutez-les en périphérie).
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Un cluster Kubernetes
|
||||
- Helm v3
|
||||
- Une image nginx BentoPDF (par ex. `ghcr.io/alam00000/bentopdf:<tag>`) qui sert sur le **port 8080**
|
||||
|
||||
## Déployer avec Helm
|
||||
|
||||
### Installer depuis ce dépôt (chart local)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Installer depuis GHCR (chart OCI)
|
||||
|
||||
Si le chart est publié sur GHCR en tant qu'artefact OCI :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## L'exposer
|
||||
|
||||
### Port-forward (test rapide)
|
||||
|
||||
```bash
|
||||
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
|
||||
```
|
||||
|
||||
### Ingress (facultatif)
|
||||
|
||||
Activez Ingress (exemple pour nginx-ingress) :
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: pdf.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Gateway API (facultatif)
|
||||
|
||||
Ce chart prend en charge `Gateway` + `HTTPRoute` de Gateway API.
|
||||
|
||||
Exemple (opérateur Gateway API de Cloudflare) :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Vérifier que les en-têtes SharedArrayBuffer fonctionnent toujours (Ingress/Gateway)
|
||||
|
||||
### Ce qui « devrait » se passer
|
||||
|
||||
La configuration nginx de BentoPDF définit les en-têtes de réponse requis. La plupart des contrôleurs Ingress/Gateway **transmettent les en-têtes de réponse en amont sans les modifier**.
|
||||
|
||||
### Ce qui peut tout casser
|
||||
|
||||
- Une politique de contrôleur ou de périphérie qui **remplace** ou **supprime** les en-têtes de réponse
|
||||
- Un middleware d'« en-têtes de sécurité » qui définit des valeurs COOP/COEP différentes
|
||||
|
||||
### Comment vérifier
|
||||
|
||||
Exécutez ceci sur votre point d'accès public :
|
||||
|
||||
```bash
|
||||
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
|
||||
```
|
||||
|
||||
Vous devriez voir :
|
||||
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### Si votre contrôleur Ingress ne les conserve pas
|
||||
|
||||
Ajoutez les en-têtes en périphérie (selon le contrôleur). Exemple pour **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;
|
||||
```
|
||||
|
||||
## Erreurs de type MIME `.mjs` (iframe vide dans Signer un PDF / remplissage de formulaire)
|
||||
|
||||
Si la console du navigateur affiche `Failed to load module script: ... non-JavaScript MIME type "application/octet-stream"` sur une requête `.mjs`, un contrôleur Ingress/Gateway placé devant le nginx de BentoPDF remplace l'en-tête `Content-Type` en amont par `application/octet-stream`.
|
||||
|
||||
Le nginx de l'image BentoPDF sert déjà `.mjs` en `application/javascript`. La solution consiste à empêcher le contrôleur de le supprimer ou de le remplacer. Pour nginx-ingress :
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
location ~* \.mjs$ {
|
||||
types {} default_type application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
Ou utilisez un filtre `ResponseHeaderModifier` dans Gateway API pour définir `Content-Type: application/javascript` sur les chemins `.mjs`. Vérifiez avec les DevTools, onglet Network : la requête `.mjs` en échec doit maintenant afficher `content-type: application/javascript`.
|
||||
|
||||
### Si vous utilisez Gateway API et voulez forcer l'ajout d'en-têtes
|
||||
|
||||
Gateway API prend en charge un filtre `ResponseHeaderModifier`. Vous pouvez l'attacher dans `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
|
||||
```
|
||||
|
||||
La prise en charge de filtres précis dépend de votre contrôleur Gateway ; si un filtre est ignoré, ajoutez plutôt les en-têtes au niveau de la périphérie ou du contrôleur.
|
||||
|
||||
## Désactiver des outils précis
|
||||
|
||||
Utilisez un ConfigMap pour désactiver des outils à l'exécution sans reconstruire l'image :
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Montez-le dans le répertoire servi :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Les identifiants d'outils correspondent à l'URL de la page sans `.html` : ouvrez un outil et regardez l'URL (par ex. `edit-pdf`, `merge-pdf`, `compress-pdf`). Les outils désactivés sont masqués de la page d'accueil, de la recherche, des raccourcis, du constructeur de workflow et de l'accès direct par URL. Consultez le [guide Docker](/fr/self-hosting/docker#desactiver-des-outils-specifiques) pour la liste complète des options.
|
||||
@@ -0,0 +1,139 @@
|
||||
# Déployer sur Netlify
|
||||
|
||||
[Netlify](https://netlify.com) propose un excellent hébergement de sites statiques avec une offre gratuite généreuse.
|
||||
|
||||
## Déploiement en un clic
|
||||
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Déploiement manuel
|
||||
|
||||
### Étape 1 : connecter le dépôt
|
||||
|
||||
1. Connectez-vous à [Netlify](https://app.netlify.com)
|
||||
2. Cliquez sur « Add new site » → « Import an existing project »
|
||||
3. Connectez votre compte GitHub
|
||||
4. Sélectionnez votre fork de BentoPDF
|
||||
|
||||
### Étape 2 : configurer les paramètres de build
|
||||
|
||||
| Paramètre | Valeur |
|
||||
| ------------------------- | --------------- |
|
||||
| Commande de build | `npm run build` |
|
||||
| Répertoire de publication | `dist` |
|
||||
| Version de Node | 18+ |
|
||||
|
||||
### Étape 3 : déployer
|
||||
|
||||
Cliquez sur « Deploy site » et attendez la fin du build.
|
||||
|
||||
## Fichier de configuration
|
||||
|
||||
Créez `netlify.toml` à la racine de votre projet :
|
||||
|
||||
```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 Important
|
||||
Les en-têtes `Cross-Origin-Embedder-Policy` et `Cross-Origin-Opener-Policy` sont nécessaires pour les conversions Word/ODT/Excel/PowerPoint vers PDF. Sans eux, `SharedArrayBuffer` n'est pas disponible et le moteur LibreOffice WASM ne parviendra pas à s'initialiser.
|
||||
|
||||
Les en-têtes `Content-Encoding: gzip` sur les fichiers `.wasm.gz` et `.data.gz` indiquent au navigateur de les décompresser automatiquement. Sans eux, le navigateur reçoit des octets gzip bruts et la compilation WASM échoue.
|
||||
:::
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
Définissez-les dans Site settings → Environment variables :
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------- | ----------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Mettre à `true` pour un build minimal |
|
||||
| `VITE_BRAND_NAME` | Nom de marque personnalisé (remplace « BentoPDF ») |
|
||||
| `VITE_BRAND_LOGO` | Chemin du logo relatif à `public/` (par ex. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Texte personnalisé du pied de page/copyright |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Langue par défaut de l'interface (par ex. `fr`, `de`, `es`) |
|
||||
|
||||
## Domaine personnalisé
|
||||
|
||||
1. Allez dans Site settings → Domain management
|
||||
2. Cliquez sur « Add custom domain »
|
||||
3. Suivez les instructions de configuration DNS
|
||||
|
||||
## Large Media
|
||||
|
||||
Pour les gros fichiers WASM, envisagez d'activer [Netlify Large Media](https://docs.netlify.com/large-media/overview/):
|
||||
|
||||
```bash
|
||||
netlify lm:setup
|
||||
git lfs track "*.wasm"
|
||||
```
|
||||
|
||||
## Dépannage
|
||||
|
||||
### La conversion Word/ODT/Excel vers PDF reste bloquée à 55 %
|
||||
|
||||
Si les conversions de documents restent bloquées à 55 %, ouvrez la console des DevTools et vérifiez :
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Si `crossOriginIsolated` vaut `false`, les en-têtes COEP/COOP de votre `netlify.toml` ne sont pas appliqués. Vérifiez que le fichier se trouve à la racine de votre projet et redéployez.
|
||||
|
||||
Si vous voyez `expected magic word 00 61 73 6d, found 1f 8b 08 08` dans la console, il manque les en-têtes `Content-Encoding: gzip` sur les fichiers `.wasm.gz`. Assurez-vous que les blocs `[[headers]]` pour `soffice.wasm.gz` et `soffice.data.gz` figurent dans votre `netlify.toml`.
|
||||
|
||||
### Échec du build
|
||||
|
||||
Vérifiez la compatibilité de la version de Node :
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
```
|
||||
|
||||
### Chargement initial lent
|
||||
|
||||
Activez l'optimisation des ressources dans Site settings → Build & deploy → Asset optimization.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Déployer avec Nginx
|
||||
|
||||
Hébergez BentoPDF sur votre propre serveur avec Nginx.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Serveur Ubuntu/Debian
|
||||
- Nginx installé
|
||||
- Certificat SSL (recommandé : Let's Encrypt)
|
||||
|
||||
## Étape 1 : construire le projet
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Pour personnaliser l'image de marque, définissez des variables d'environnement avant le build :
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Étape 2 : copier les fichiers
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Étape 3 : configuration de Nginx
|
||||
|
||||
Créez `/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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Étape 4 : activer le site
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Étape 5 : SSL avec Let's Encrypt
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
## Déploiement dans un sous-répertoire
|
||||
|
||||
Pour héberger sous `/pdf/` :
|
||||
|
||||
```nginx
|
||||
location /pdf/ {
|
||||
alias /var/www/bentopdf/;
|
||||
try_files $uri $uri/ /pdf/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
Construisez avec :
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
## Optimisation des performances
|
||||
|
||||
Ajoutez à `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;
|
||||
}
|
||||
```
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Le WASM ne se charge pas
|
||||
|
||||
Vérifiez que le type MIME est défini :
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
### Signer un PDF ou le remplissage de formulaire affiche un lecteur vide (erreur MIME `.mjs`)
|
||||
|
||||
Si la console du navigateur affiche :
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…et que la requête en échec se termine par `.mjs`, il manque à votre nginx la correspondance `.mjs → application/javascript`. Le lecteur PDF intégré (utilisé par Signer un PDF, le remplissage de formulaire et quelques autres outils) fournit des modules ES avec l'extension `.mjs`, et le fichier `mime.types` par défaut de nginx ne les inclut pas.
|
||||
|
||||
Corrigez-le en ajoutant `mjs` à la ligne `application/javascript` existante dans `/etc/nginx/mime.types` :
|
||||
|
||||
```nginx
|
||||
application/javascript js mjs;
|
||||
```
|
||||
|
||||
> N'ajoutez PAS une _seconde_ ligne `application/javascript` : nginx analyse `mime.types` ligne par ligne et une seule correspondance type-extension l'emporte, donc une seconde ligne est ignorée silencieusement sur certaines versions. Modifiez la ligne existante pour ajouter `mjs` à côté de `js`.
|
||||
|
||||
Ou redéfinissez-le localement dans votre bloc server (sans modifier le fichier global) :
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
Rechargez nginx après la modification :
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Pour confirmer : dans les DevTools du navigateur, onglet Network, repérez la requête `.mjs` en échec et vérifiez l'en-tête de réponse `Content-Type`. Il doit maintenant valoir `application/javascript` et non `application/octet-stream`.
|
||||
|
||||
### La conversion Word/ODT/Excel vers PDF ne fonctionne pas
|
||||
|
||||
LibreOffice WASM nécessite `SharedArrayBuffer`, qui exige les en-têtes `Cross-Origin-Embedder-Policy` et `Cross-Origin-Opener-Policy`. Il faut aussi un contexte sécurisé : `http://localhost` fonctionne pour les tests locaux, mais `http://192.168.x.x` ou d'autres IP du réseau local nécessitent généralement HTTPS. Notez que les directives `add_header` d'un bloc `location` **remplacent** celles du niveau server, elles ne fusionnent pas. Chaque bloc `location` ayant son propre `add_header` doit inclure les en-têtes COEP/COOP.
|
||||
|
||||
Vérifiez avec :
|
||||
|
||||
```bash
|
||||
curl -I https://your-domain.com/ | grep -i cross-origin
|
||||
```
|
||||
|
||||
Si vous utilisez un reverse proxy devant nginx, assurez-vous qu'il conserve ces en-têtes.
|
||||
|
||||
### 502 Bad Gateway
|
||||
|
||||
Consultez les journaux d'erreurs de Nginx :
|
||||
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
# Déployer sur Vercel
|
||||
|
||||
[Vercel](https://vercel.com) offre l'expérience de déploiement la plus simple pour les sites statiques.
|
||||
|
||||
## Déploiement en un clic
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Déploiement manuel
|
||||
|
||||
### Étape 1 : forker le dépôt
|
||||
|
||||
Forkez [bentopdf/bentopdf](https://github.com/alam00000/bentopdf) vers votre compte GitHub.
|
||||
|
||||
### Étape 2 : importer dans Vercel
|
||||
|
||||
1. Rendez-vous sur [vercel.com/new](https://vercel.com/new)
|
||||
2. Sélectionnez votre dépôt forké
|
||||
3. Configurez le projet :
|
||||
|
||||
| Paramètre | Valeur |
|
||||
| ----------------------- | --------------- |
|
||||
| Préréglage du framework | Vite |
|
||||
| Commande de build | `npm run build` |
|
||||
| Répertoire de sortie | `dist` |
|
||||
| Commande d'installation | `npm install` |
|
||||
|
||||
### Étape 3 : variables d'environnement (facultatif)
|
||||
|
||||
Ajoutez-les si nécessaire :
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------- | ----------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Mettre à `true` pour une interface minimale |
|
||||
| `VITE_BRAND_NAME` | Nom de marque personnalisé (remplace « BentoPDF ») |
|
||||
| `VITE_BRAND_LOGO` | Chemin du logo relatif à `public/` (par ex. `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Texte personnalisé du pied de page/copyright |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Langue par défaut de l'interface (par ex. `fr`, `de`, `es`) |
|
||||
|
||||
### Étape 4 : déployer
|
||||
|
||||
Cliquez sur « Deploy » et attendez la fin du build.
|
||||
|
||||
## Domaine personnalisé
|
||||
|
||||
1. Ouvrez les paramètres de votre projet
|
||||
2. Allez dans « Domains »
|
||||
3. Ajoutez votre domaine personnalisé
|
||||
4. Configurez le DNS comme indiqué
|
||||
|
||||
## Limitations
|
||||
|
||||
::: warning Fichiers volumineux
|
||||
Les fonctions serverless de Vercel ont une limite de 50 Mo. BentoPDF étant un site statique, cela ne devrait pas vous concerner, mais les modules WASM sont volumineux (~100 Mo au total). Assurez-vous qu'ils sont servis depuis le dossier `/public`.
|
||||
:::
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Délai d'attente du build dépassé
|
||||
|
||||
Si les builds dépassent le délai, essayez :
|
||||
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist"
|
||||
}
|
||||
```
|
||||
|
||||
### Erreur 404 à l'actualisation
|
||||
|
||||
Ajoutez un fichier `vercel.json` pour le routage SPA :
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
```
|
||||
|
||||
### La conversion Word/ODT/Excel vers PDF ne fonctionne pas
|
||||
|
||||
Les conversions LibreOffice WASM nécessitent `SharedArrayBuffer`, qui exige ces en-têtes de réponse sur toutes les pages :
|
||||
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
|
||||
Les fichiers précompressés `.wasm.gz` et `.data.gz` ont aussi besoin de `Content-Encoding: gzip` pour que le navigateur les décompresse.
|
||||
|
||||
Ajoutez-les à votre `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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Pour vérifier, ouvrez la console des DevTools et exécutez `console.log(window.crossOriginIsolated)` : le résultat doit être `true`.
|
||||
Reference in new issue
Block a user