mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-11 09:23:36 +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 @@
|
||||
# Разгортванне з Apache
|
||||
|
||||
Размясціце BentoPDF з дапамогай Apache HTTP Server.
|
||||
|
||||
## Патрабаванні
|
||||
|
||||
- Apache 2.4+
|
||||
- уключаны mod_rewrite
|
||||
- SSL-сертыфікат (рэкамендуецца)
|
||||
|
||||
## Крок 1: Збярыце праект
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Каб змяніць брэндынг, задайце зменныя асяроддзя перад зборкай:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Крок 2: Скапіруйце файлы
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Крок 3: Канфігурацыя Apache
|
||||
|
||||
Стварыце `/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>
|
||||
```
|
||||
|
||||
## Крок 4: .htaccess для маршрутызацыі
|
||||
|
||||
Стварыце `/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
|
||||
```
|
||||
|
||||
## Крок 5: Уключыце неабходныя модулі
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
sudo a2enmod ssl
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod expires
|
||||
sudo a2enmod deflate
|
||||
```
|
||||
|
||||
## Крок 6: Уключыце сайт
|
||||
|
||||
```bash
|
||||
sudo a2ensite bentopdf.conf
|
||||
sudo apache2ctl configtest
|
||||
sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
## Разгортванне ў падкаталогу
|
||||
|
||||
Каб размясціць па адрасе `/pdf/`:
|
||||
|
||||
1. Збярыце з базавым URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
2. Абнавіце `.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
|
||||
```
|
||||
|
||||
## Вырашэнне праблем
|
||||
|
||||
### Памылкі 404 для WASM
|
||||
|
||||
Пераканайцеся, што MIME-тып настроены:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Падпісаць PDF або Запоўніць форму PDF паказвае пусты прагляднік (памылка MIME `.mjs`)
|
||||
|
||||
Калі ў кансолі браўзера з'яўляецца:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…і запыт з памылкай заканчваецца на `.mjs`, у вашай канфігурацыі Apache адсутнічае адпаведнасць `.mjs → application/javascript`. Убудаваны прагляднік PDF пастаўляецца з файламі ES-модуляў з пашырэннем `.mjs`, а стандартны `mime.types` Apache па змаўчанні іх не ўключае.
|
||||
|
||||
Дадайце ў VirtualHost або `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/javascript .mjs
|
||||
```
|
||||
|
||||
Затым перазагрузіце Apache:
|
||||
|
||||
```bash
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
```
|
||||
|
||||
Каб праверыць: у DevTools браўзера → укладка Network знайдзіце запыт `.mjs` з памылкай і праверце загаловак адказу `Content-Type`. Цяпер ён павінен быць `application/javascript`, а не `application/octet-stream`.
|
||||
|
||||
### Rewrite не працуе
|
||||
|
||||
Праверце, што mod_rewrite уключаны:
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
### Word/ODT/Excel у PDF не працуе
|
||||
|
||||
LibreOffice WASM патрабуе `SharedArrayBuffer`, для якога патрэбныя гэтыя загалоўкі:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
Таксама патрэбны бяспечны кантэкст. `http://localhost` падыходзіць для лакальнага тэсціравання, але `http://192.168.x.x` і іншыя IP-адрасы лакальнай сеткі звычайна патрабуюць HTTPS. Калі загалоўкі ёсць, але `window.crossOriginIsolated` усё яшчэ `false`, праверце, ці не адкрываецца старонка праз звычайны HTTP з адраса, які не з'яўляецца loopback.
|
||||
|
||||
Папярэдне сціснутым файлам `.wasm.gz` і `.data.gz` таксама патрэбны правільны `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>
|
||||
```
|
||||
|
||||
Пераканайцеся, што `mod_headers` уключаны: `sudo a2enmod headers`
|
||||
|
||||
### Адмоўлена ў доступе
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
sudo chmod -R 755 /var/www/bentopdf
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
# Разгортванне на AWS S3 + CloudFront
|
||||
|
||||
Размясціце BentoPDF на AWS для максімальнага кантролю і маштабаванасці.
|
||||
|
||||
## Архітэктура
|
||||
|
||||
```
|
||||
User → CloudFront (CDN) → S3 (Static Files)
|
||||
```
|
||||
|
||||
## Крок 1: Стварыце бакет 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
|
||||
```
|
||||
|
||||
## Крок 2: Зборка і загрузка
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Крок 3: Стварыце дыстрыбуцыю CloudFront
|
||||
|
||||
```bash
|
||||
aws cloudfront create-distribution \
|
||||
--origin-domain-name your-bentopdf-bucket.s3.amazonaws.com \
|
||||
--default-root-object index.html
|
||||
```
|
||||
|
||||
Або выкарыстайце AWS Console:
|
||||
|
||||
1. Перайдзіце ў CloudFront → Create distribution
|
||||
2. Origin domain: выберыце ваш бакет S3
|
||||
3. Уключыце "Origin Access Control"
|
||||
4. Default root object: `index.html`
|
||||
5. Стварыце дыстрыбуцыю
|
||||
|
||||
## Крок 3b: Палітыка загалоўкаў адказу (абавязкова для LibreOffice WASM)
|
||||
|
||||
Канвертацыі на аснове LibreOffice (Word, Excel, PowerPoint у PDF) патрабуюць `SharedArrayBuffer`, для якога патрэбныя пэўныя загалоўкі адказу. Стварыце CloudFront Response Headers Policy:
|
||||
|
||||
1. Перайдзіце ў CloudFront → Policies → Response headers
|
||||
2. Стварыце ўласную палітыку з гэтымі загалоўкамі:
|
||||
|
||||
| Загаловак | Значэнне |
|
||||
| ------------------------------ | -------------- |
|
||||
| `Cross-Origin-Embedder-Policy` | `require-corp` |
|
||||
| `Cross-Origin-Opener-Policy` | `same-origin` |
|
||||
| `Cross-Origin-Resource-Policy` | `cross-origin` |
|
||||
|
||||
3. Прымацуйце палітыку да default cache behavior вашай дыстрыбуцыі
|
||||
|
||||
Або праз 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}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Крок 3c: Метаданыя S3 для папярэдне сціснутых файлаў WASM
|
||||
|
||||
Файлы LibreOffice WASM папярэдне сціснутыя (`.wasm.gz`, `.data.gz`). Задайце правільныя Content-Type і Content-Encoding, каб браўзеры іх распакоўвалі:
|
||||
|
||||
```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 Важна
|
||||
Без палітыкі загалоўкаў адказу `SharedArrayBuffer` недаступны, і канвертацыі LibreOffice WASM завісаюць на ~55%. Без правільнага Content-Encoding для файлаў `.gz` браўзер атрымлівае сырыя байты gzip, і кампіляцыя WASM не ўдаецца.
|
||||
:::
|
||||
|
||||
## Крок 4: Палітыка бакета S3
|
||||
|
||||
Дазвольце CloudFront доступ да бакета:
|
||||
|
||||
```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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Крок 5: Уласныя старонкі памылак
|
||||
|
||||
Наладзьце 404 так, каб вяртаўся `index.html` для SPA-маршрутызацыі:
|
||||
|
||||
1. CloudFront → Error pages
|
||||
2. Стварыце custom error response:
|
||||
- HTTP error code: 404
|
||||
- Response page path: `/index.html`
|
||||
- HTTP response code: 200
|
||||
|
||||
## Ацэнка кошту
|
||||
|
||||
| Рэсурс | Прыблізны кошт |
|
||||
| ------------------------- | -------------- |
|
||||
| Сховішча S3 (~500MB) | ~$0.01/месяц |
|
||||
| CloudFront (1TB трафіку) | ~$85/месяц |
|
||||
| CloudFront (10GB трафіку) | ~$0.85/месяц |
|
||||
|
||||
::: tip
|
||||
Выкарыстоўвайце S3 Intelligent Tiering, каб зэканоміць на файлах, да якіх рэдка звяртаюцца.
|
||||
:::
|
||||
|
||||
## Аўтаматызацыя з 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 @@
|
||||
# Разгортванне на Cloudflare Pages
|
||||
|
||||
[Cloudflare Pages](https://pages.cloudflare.com) прапануе хуткі глабальны хостынг статычных сайтаў з неабмежаванай прапускной здольнасцю.
|
||||
|
||||
## Хуткае разгортванне
|
||||
|
||||
1. Перайдзіце ў [Cloudflare Pages](https://dash.cloudflare.com/?to=/:account/pages)
|
||||
2. Націсніце "Create a project"
|
||||
3. Падключыце ваш рэпазіторый GitHub
|
||||
|
||||
## Канфігурацыя зборкі
|
||||
|
||||
| Параметр | Значэнне |
|
||||
| --------------------- | --------------- |
|
||||
| Framework preset | None |
|
||||
| Каманда зборкі | `npm run build` |
|
||||
| Каталог вываду зборкі | `dist` |
|
||||
| Каранёвы каталог | `/` |
|
||||
|
||||
## Зменныя асяроддзя
|
||||
|
||||
Дадайце іх у Settings → Environment variables:
|
||||
|
||||
| Зменная | Значэнне |
|
||||
| ----------------------- | ----------------------------------------------------------- |
|
||||
| `NODE_VERSION` | `18` |
|
||||
| `SIMPLE_MODE` | `false` (неабавязкова) |
|
||||
| `VITE_BRAND_NAME` | Уласная назва брэнда (неабавязкова) |
|
||||
| `VITE_BRAND_LOGO` | Шлях да лагатыпа адносна `public/` (неабавязкова) |
|
||||
| `VITE_FOOTER_TEXT` | Уласны тэкст футэра/аўтарскіх правоў (неабавязкова) |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Мова інтэрфейсу па змаўчанні, напрыклад `fr` (неабавязкова) |
|
||||
|
||||
## Файл канфігурацыі
|
||||
|
||||
Стварыце `_headers` у тэчцы `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 Важна
|
||||
Загалоўкі `Cross-Origin-Embedder-Policy` і `Cross-Origin-Opener-Policy` абавязковыя для канвертацыі Word/ODT/Excel/PowerPoint у PDF. Без іх `SharedArrayBuffer` недаступны, і рухавік LibreOffice WASM не здолее ініцыялізавацца.
|
||||
:::
|
||||
|
||||
Стварыце `_redirects` для SPA-маршрутызацыі:
|
||||
|
||||
```
|
||||
/* /index.html 200
|
||||
```
|
||||
|
||||
## Уласны дамен
|
||||
|
||||
1. Перайдзіце ў ваш праект Pages
|
||||
2. Націсніце "Custom domains"
|
||||
3. Дадайце ваш дамен
|
||||
4. Cloudflare аўтаматычна наладзіць DNS, калі дамен знаходзіцца ў Cloudflare
|
||||
|
||||
## Перавагі
|
||||
|
||||
- **Бясплатная неабмежаваная прапускная здольнасць**
|
||||
- **Глабальны CDN** з 300+ пагранічнымі вузламі
|
||||
- **Аўтаматычны HTTPS**
|
||||
- **Папярэднія разгортванні** для pull request
|
||||
- **Хуткія зборкі**
|
||||
|
||||
## Вырашэнне праблем
|
||||
|
||||
### Загрузка вялікіх файлаў
|
||||
|
||||
Cloudflare Pages падтрымлівае файлы да 25 MB. З модулямі WASM праблем быць не павінна, але калі вы ўпраецеся ў ліміты, паспрабуйце:
|
||||
|
||||
```bash
|
||||
# Split large files during build
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Ліміты памеру Worker
|
||||
|
||||
Калі вы выкарыстоўваеце Cloudflare Workers для пашыранай маршрутызацыі, улічвайце ліміт 1 MB на бясплатных планах.
|
||||
|
||||
## CORS-проксі Worker (для лічбавых подпісаў)
|
||||
|
||||
Інструменту лічбавага подпісу патрэбны CORS-проксі, каб атрымліваць ланцужкі сертыфікатаў. Разгарніце ўключаны worker:
|
||||
|
||||
```bash
|
||||
cd cloudflare
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Функцыі бяспекі
|
||||
|
||||
| Функцыя | Апісанне |
|
||||
| --------------------------- | --------------------------------- |
|
||||
| **Абмежаванні URL** | Дазволены толькі URL сертыфікатаў |
|
||||
| **Ліміт памеру файла** | Максімум 10MB на запыт |
|
||||
| **Абмежаванне частаты** | 60 запытаў/IP/хв (патрабуе KV) |
|
||||
| **Блакіроўка прыватных IP** | Блакіруе localhost і ўнутраныя IP |
|
||||
|
||||
### Уключэнне абмежавання частаты
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Зборка з URL проксі
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
Або з 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 .
|
||||
```
|
||||
|
||||
> **Заўвага:** наладка подпісу HMAC апісана ў [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required).
|
||||
@@ -0,0 +1,180 @@
|
||||
# CORS-проксі для атрымання сертыфікатаў
|
||||
|
||||
Інструмент лічбавага подпісу выкарыстоўвае CORS-проксі, каб атрымліваць сертыфікаты выдаўцоў ад знешніх цэнтраў сертыфікацыі (CA). Гэта неабходна, бо многія серверы CA не ўключаюць загалоўкі CORS у свае адказы, што не дазваляе атрымліваць іх непасрэдна з браўзера.
|
||||
|
||||
Акрамя таго, многія серверы CA аддаюць сертыфікаты праз звычайны HTTP. Калі ваш экзэмпляр BentoPDF працуе праз HTTPS, браўзеры блакіруюць такія HTTP-запыты (палітыка змешанага кантэнту). CORS-проксі вырашае абедзве праблемы, накіроўваючы запыты праз HTTPS-адрас з правільнымі загалоўкамі.
|
||||
|
||||
## Як гэта працуе
|
||||
|
||||
Пры подпісе PDF сертыфікатам:
|
||||
|
||||
1. Бібліятэка `zgapdfsigner` спрабуе пабудаваць поўны ланцужок сертыфікатаў
|
||||
2. Яна атрымлівае сертыфікаты выдаўцоў па URL, укладзеных у пашырэнне AIA (Authority Information Access) вашага сертыфіката
|
||||
3. Гэтыя запыты праходзяць праз CORS-проксі, які дадае неабходныя загалоўкі `Access-Control-Allow-Origin`
|
||||
4. Проксі вяртае даныя сертыфіката браўзеру
|
||||
|
||||
## Уласнае размяшчэнне CORS-проксі
|
||||
|
||||
Калі вы размяшчаеце BentoPDF у сябе, вам трэба разгарнуць уласны CORS-проксі, каб лічбавыя подпісы працавалі з сертыфікатамі, якія патрабуюць атрымання ланцужка.
|
||||
|
||||
### Варыянт 1: Cloudflare Workers (рэкамендуецца)
|
||||
|
||||
1. **Усталюйце Wrangler CLI**:
|
||||
|
||||
```bash
|
||||
npm install -g wrangler
|
||||
```
|
||||
|
||||
2. **Увайдзіце ў Cloudflare**:
|
||||
|
||||
```bash
|
||||
wrangler login
|
||||
```
|
||||
|
||||
3. **Скланіруйце BentoPDF і абнавіце дазволеныя крыніцы**:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf/cloudflare
|
||||
```
|
||||
|
||||
Адкрыйце `cors-proxy-worker.js` і змяніце масіў `ALLOWED_ORIGINS` на ваш дамен:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = [
|
||||
'https://your-domain.com',
|
||||
'https://www.your-domain.com',
|
||||
];
|
||||
```
|
||||
|
||||
::: warning Важна
|
||||
Без гэтай змены проксі будзе адхіляць усе запыты з вашага сайта з памылкай **403 Forbidden**. Па змаўчанні дазволены толькі запыты з `bentopdf.com`.
|
||||
:::
|
||||
|
||||
4. **Разгарніце проксі**:
|
||||
|
||||
```bash
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Запішыце URL вашага worker (напрыклад, `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
|
||||
|
||||
5. **Перасабярыце BentoPDF з URL проксі**:
|
||||
|
||||
Калі выкарыстоўваеце 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 .
|
||||
```
|
||||
|
||||
Калі збіраеце з зыходнага кода:
|
||||
|
||||
```bash
|
||||
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
|
||||
```
|
||||
|
||||
### Варыянт 2: Уласны серверны проксі
|
||||
|
||||
Вы таксама можаце стварыць уласны адрас проксі. Патрабаванні:
|
||||
|
||||
1. Прымаць GET-запыты з параметрам `url`
|
||||
2. Атрымліваць URL з вашага сервера (на баку сервера абмежаванняў CORS няма)
|
||||
3. Вяртаць адказ з гэтымі загалоўкамі:
|
||||
- `Access-Control-Allow-Origin: https://your-domain.com`
|
||||
- `Access-Control-Allow-Methods: GET, OPTIONS`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
|
||||
Прыклад рэалізацыі на 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' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Пытанні бяспекі
|
||||
|
||||
Уключаны Cloudflare Worker мае некалькі мер бяспекі:
|
||||
|
||||
| Функцыя | Апісанне |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| **Праверка крыніцы** | Дазваляе толькі запыты з даменаў, пералічаных у `ALLOWED_ORIGINS` |
|
||||
| **Абмежаванні URL** | Дазваляе толькі URL сертыфікатаў (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`) |
|
||||
| **Блакіроўка прыватных IP** | Блакіруе прыватныя дыяпазоны IPv4/IPv6, link-local, loopback, дзесятковыя IP і хмарныя метаданыя |
|
||||
| **Бяспека Content-Type** | Вяртае толькі бяспечныя MIME-тыпы сертыфікатаў, блакіруе падмену content-type з крыніцы |
|
||||
| **Ліміт памеру файла** | Перадае адказ патокам з лімітам 10MB, перарывае загрузку пры перавышэнні |
|
||||
| **Абмежаванне частаты** | 60 запытаў у хвіліну на IP або IPv6 /64 (патрабуе KV, пры памылках KV прапускае запыты) |
|
||||
| **Подпісы HMAC** | Неабавязковы подпіс на баку кліента (стрымлівае выпадковыя злоўжыванні) |
|
||||
|
||||
### DNS rebinding (уласнае размяшчэнне па-за Cloudflare)
|
||||
|
||||
Worker адхіляе любы URL, які разрознаецца ў прыватны або ўнутраны IP. Ён не можа выкарыстоўваць фіксаваны спіс дазволеных хостаў, бо URL ланцужкоў сертыфікатаў вядуць да таго CA, які выдаў сертыфікат (FNMT, карпаратыўныя CA і г.д.), таму адрасы загадзя невядомыя. Гэта пакідае невялікае акно для rebinding: той, хто кіруе DNS свайго дамена, можа аддаць публічны IP для праверкі бяспекі, а праз імгненне прыватны для сапраўднага запыту.
|
||||
|
||||
На Cloudflare гэтым немагчыма скарыстацца: Workers не маюць доступу да прыватных IP і адраса метаданых `169.254.169.254`, таму rebind нікуды не вядзе. Проста не давайце worker шляху ўнутр: не падключайце Tunnel, Service або прывязку да прыватнай сеткі (яму патрэбен толькі `RATE_LIMIT_KV`).
|
||||
|
||||
Калі вы запускаеце яго па-за Cloudflare (workerd, Node і падобнае) унутры VPC, асяроддзе выканання _можа_ дасягнуць унутраных IP. Пастаўце перад ім брандмаўэр выходнага трафіку і заблакіруйце прыватныя дыяпазоны: `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` і іх аналагі IPv6. Па-за Cloudflare код праграмы не можа гэтага забяспечыць.
|
||||
|
||||
## Адключэнне проксі
|
||||
|
||||
Калі вы не хочаце выкарыстоўваць CORS-проксі, задайце зменнай асяроддзя пусты радок:
|
||||
|
||||
```
|
||||
VITE_CORS_PROXY_URL=
|
||||
```
|
||||
|
||||
**Заўвага**: без проксі подпіс сертыфікатамі, якія патрабуюць знешняга атрымання ланцужка (напрыклад, FNMT або некаторыя карпаратыўныя CA), завершыцца памылкай "Failed to fetch".
|
||||
|
||||
## Вырашэнне праблем
|
||||
|
||||
### "Signing error: TypeError: Failed to fetch"
|
||||
|
||||
Звычайна гэта азначае адно з наступнага:
|
||||
|
||||
1. **CORS-проксі не настроены**: задайце `VITE_CORS_PROXY_URL` і перасабярыце
|
||||
2. **Змешаны кантэнт заблакіраваны**: ваш сайт працуе праз HTTPS, а URL выдаўца сертыфіката праз HTTP. CORS-проксі гэта вырашае.
|
||||
3. **CORS-проксі адхіляе вашу крыніцу**: праверце, што ваш дамен ёсць у масіве `ALLOWED_ORIGINS` у `cors-proxy-worker.js`
|
||||
|
||||
### "403 Forbidden" ад проксі
|
||||
|
||||
Вашага дамена няма ў спісе `ALLOWED_ORIGINS`. Адрэдагуйце `cors-proxy-worker.js`:
|
||||
|
||||
```js
|
||||
const ALLOWED_ORIGINS = ['https://your-domain.com'];
|
||||
```
|
||||
|
||||
Затым разгарніце нанова: `npx wrangler deploy`
|
||||
|
||||
### Праверка проксі
|
||||
|
||||
```bash
|
||||
curl -H "Origin: https://your-domain.com" \
|
||||
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"
|
||||
```
|
||||
|
||||
### Сертыфікаты, якія працуюць без проксі
|
||||
|
||||
Некаторыя сертыфікаты ўключаюць поўны ланцужок у файл P12/PFX і не патрабуюць знешняга атрымання:
|
||||
|
||||
- Самападпісаныя сертыфікаты
|
||||
- Некаторыя камерцыйныя CA, якія дадаюць прамежкавыя сертыфікаты
|
||||
- Сертыфікаты, у якія вы ўручную сабралі поўны ланцужок
|
||||
@@ -0,0 +1,622 @@
|
||||
# Разгортванне з Docker / Podman
|
||||
|
||||
Самы просты спосаб размясціць BentoPDF у сябе ў працоўным асяроддзі.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Неабходныя загалоўкі для канвертацыі офісных файлаў**
|
||||
>
|
||||
> Інструменты на аснове LibreOffice (канвертацыя Word, Excel, PowerPoint) патрабуюць гэтых HTTP-загалоўкаў для падтрымкі `SharedArrayBuffer`:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Старонка таксама павінна аддавацца ў бяспечным кантэксце. `http://localhost` падыходзіць для лакальнага тэсціравання, але `http://192.168.x.x` і іншыя IP-адрасы лакальнай сеткі звычайна не, таму канвертацыя офісных файлаў праз звычайны HTTP не спрацуе нават пры наяўнасці загалоўкаў.
|
||||
>
|
||||
> Афіцыйныя вобразы кантэйнераў уключаюць гэтыя загалоўкі. Калі вы выкарыстоўваеце зваротны проксі (Traefik, Caddy і г.д.), пераканайцеся, што гэтыя загалоўкі захоўваюцца або дадаюцца, і выкарыстоўвайце HTTPS для доступу не праз loopback.
|
||||
|
||||
> [!TIP]
|
||||
> **Карыстальнікам Podman:** усе каманды `docker` працуюць з Podman, калі замяніць `docker` на `podman`, а `docker-compose` на `podman-compose`.
|
||||
|
||||
## Хуткі старт
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF пастаўляецца ў дзвюх зборках:
|
||||
>
|
||||
> - **Self-Hosted build**: `ghcr.io/alam00000/bentopdf-simple:latest`. Усе інструменты PDF **без** маркетынгавага афармлення (без галоўнага блока, FAQ, водгукаў, футэра). Выкарыстоўвайце яе для ўнутраных, камандных і арганізацыйных разгортванняў. Гэта **не** скарочаная "lite" версія.
|
||||
> - **Commercial build**: `ghcr.io/alam00000/bentopdf:latest`. Поўны маркетынгавы сайт, які выкарыстоўвае сам bentopdf.com і ўладальнікі камерцыйнай ліцэнзіі для публічных разгортванняў.
|
||||
>
|
||||
> Калі сумняваецеся, выкарыстоўвайце Self-Hosted build.
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker run -d \
|
||||
--name bentopdf \
|
||||
-p 3000:8080 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
|
||||
# Podman
|
||||
podman run -d \
|
||||
--name bentopdf \
|
||||
-p 3000:8080 \
|
||||
ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
## Docker Compose / Podman Compose
|
||||
|
||||
Стварыце `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
|
||||
```
|
||||
|
||||
Запусціце:
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Зборка ўласнага вобраза
|
||||
|
||||
```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;"]
|
||||
```
|
||||
|
||||
Збярыце і запусціце:
|
||||
|
||||
```bash
|
||||
docker build -t bentopdf:custom .
|
||||
docker run -d -p 3000:8080 bentopdf:custom
|
||||
```
|
||||
|
||||
## Зменныя асяроддзя
|
||||
|
||||
| Зменная | Апісанне | Па змаўчанні |
|
||||
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Зборка варыянта Self-Hosted (хавае маркетынгавае афармленне bentopdf.com: галоўны блок, FAQ, водгукі, футэр). Усе інструменты PDF застаюцца. | `false` |
|
||||
| `BASE_URL` | Разгортванне ў падкаталог | `/` |
|
||||
| `VITE_WASM_PYMUPDF_URL` | URL модуля PyMuPDF WASM | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/` |
|
||||
| `VITE_WASM_GS_URL` | URL модуля Ghostscript WASM | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm@0.1.1/assets/` |
|
||||
| `VITE_WASM_CPDF_URL` | URL модуля CoherentPDF WASM | `https://cdn.jsdelivr.net/npm/coherentpdf@2.5.5/dist/` |
|
||||
| `VITE_TESSERACT_WORKER_URL` | URL скрыпта воркера OCR | _(пуста; CDN Tesseract.js па змаўчанні)_ |
|
||||
| `VITE_TESSERACT_CORE_URL` | Каталог асноўнага асяроддзя выканання OCR | _(пуста; CDN Tesseract.js па змаўчанні)_ |
|
||||
| `VITE_TESSERACT_LANG_URL` | Каталог traineddata OCR | _(пуста; CDN Tesseract.js па змаўчанні)_ |
|
||||
| `VITE_TESSERACT_AVAILABLE_LANGUAGES` | Мовы OCR праз коску, даступныя ў інтэрфейсе | _(пуста; поўны каталог)_ |
|
||||
| `VITE_OCR_FONT_BASE_URL` | Каталог шрыфтоў тэкставага слоя OCR | _(пуста; аддаленыя URL шрыфтоў Noto)_ |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Мова інтэрфейсу па змаўчанні | `en` |
|
||||
| `VITE_BRAND_NAME` | Уласная назва брэнда | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Шлях да лагатыпа адносна `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Уласны тэкст футэра/аўтарскіх правоў | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | ID інструментаў праз коску, якія трэба схаваць | _(пуста; усе інструменты ўключаны)_ |
|
||||
| `DISABLE_GITHUB_STARS` | Не загружаць колькасць зорак GitHub пры загрузцы старонкі і прыбраць `api.github.com` з CSP. Зборкі Simple Mode заўсёды гэта прапускаюць. | `false` |
|
||||
| `SITE_URL` | Публічны URL для спасылак canonical, hreflang і sitemap. Пакіньце значэнне па змаўчанні, калі не хочаце, каб пошукавыя сістэмы лічылі ваша разгортванне асобным сайтам. | `https://www.bentopdf.com` |
|
||||
| `ALLOW_INDEXING` | З уласным `SITE_URL` кожная старонка пазначаецца `noindex`, каб ваша копія не канкурыравала з bentopdf.com у выніках пошуку. Задайце `true`, каб дазволіць індэксацыю. | `false` |
|
||||
|
||||
URL модуляў WASM загадзя настроены на CDN па змаўчанні: усе пашыраныя функцыі працуюць адразу. Перавызначце іх для ізаляваных або ўласных разгортванняў.
|
||||
|
||||
### Пошукавыя сістэмы
|
||||
|
||||
Па змаўчанні кожная старонка паказвае пошукавым сістэмам bentopdf.com як арыгінал, таму ваша разгортванне не з'яўляецца як дублікат. Для прыватнага або ўнутранага экзэмпляра нічога мяняць не трэба.
|
||||
|
||||
- **Цалкам выключыць экзэмпляр з вынікаў пошуку:** запусціце кантэйнер з `-e ROBOTS_NOINDEX=true`. Патрэбны толькі перазапуск, без перазборкі.
|
||||
- **Зрабіць экзэмпляр асобным сайтам у выніках пошуку:** збярыце з `--build-arg SITE_URL=https://your-domain.example --build-arg ALLOW_INDEXING=true`. Без `ALLOW_INDEXING=true` уласны `SITE_URL` пазначае кожную старонку як `noindex`.
|
||||
|
||||
### Content-Security-Policy
|
||||
|
||||
Вобраз nginx пастаўляецца з актыўным загалоўкам `Content-Security-Policy`. Дырэктывы CSP `script-src`, `connect-src` і `font-src` **генеруюцца падчас зборкі** з URL-зменных `VITE_*` вышэй: усе хасты, перададзеныя праз `--build-arg`, аўтаматычна дадаюцца ў палітыку.
|
||||
|
||||
У выніку:
|
||||
|
||||
- Калі вы перавызначаеце `VITE_CORS_PROXY_URL` або `VITE_WASM_*_URL` падчас зборкі, CSP аўтаматычна дазваляе гэтыя крыніцы, дадатковая наладка не патрэбна.
|
||||
- Для URL на `cdn.jsdelivr.net` `script-src` дазваляе толькі дакладную тэчку пакета з версіяй, напрыклад `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.16/`, а не ўвесь CDN. jsDelivr аддае любы публічны пакет npm або рэпазіторый GitHub, таму дазвол на ўвесь хост дазволіў бы любому з іх выконвацца ў вашым экзэмпляры. URL на іншых хастах дазваляюцца па крыніцы (origin).
|
||||
- Калі вы наладжваеце ўласныя URL WASM _падчас выканання_ на старонцы пашыраных налад праграмы, гэтых крыніц **няма** ў CSP, і браўзер заблакіруе запыты да іх. Гэта датычыцца і іншай версіі таго ж пакета на `cdn.jsdelivr.net`, бо дазволена толькі версія, зададзеная падчас зборкі. Наладка падчас выканання прызначана для эксперыментаў; для пастаянных уласных URL задайце адпаведны build arg `VITE_*`.
|
||||
- У ізаляваных разгортваннях, якія перавызначаюць усе тры значэнні `VITE_WASM_*_URL`, гэтыя тэчкі пакетаў `cdn.jsdelivr.net` таксама выдаляюцца з CSP (кожнае значэнне па змаўчанні замяняецца, а не дапаўняецца). Значэнні Tesseract OCR па змаўчанні на `cdn.jsdelivr.net` застаюцца ў палітыцы, пакуль вы не зададзіце і тры значэнні `VITE_TESSERACT_*`. Аналагічна, `VITE_CORS_PROXY_URL` замяняе публічнае значэнне па змаўчанні `bentopdf-cors-proxy.bentopdf.workers.dev`.
|
||||
|
||||
CSP уключае `'unsafe-eval'` у `script-src`, бо асяроддзе выканання LibreOffice WASM (выкарыстоўваецца інструментамі канвертацыі Word/Excel/PowerPoint) і бібліятэка відарысаў wasm-vips (выкарыстоўваецца PDF у TIFF) кампілююць унутраны код дыспетчарызацыі праз `new Function()`. Яго выдаленне зламае гэтыя інструменты. Калі вы збіраеце ў `SIMPLE_MODE` (без LibreOffice) і вам не патрэбны PDF у TIFF, можна ўручную адрэдагаваць згенераваны `security-headers.conf` і прыбраць `'unsafe-eval'` для больш строгай палітыкі.
|
||||
|
||||
Для OCR пакіньце зменныя `VITE_TESSERACT_*` пустымі, каб выкарыстоўваць анлайн-рэсурсы па змаўчанні, або задайце ўсе тры разам для ўласнага/аўтаномнага OCR. Частковыя перавызначэнні OCR адхіляюцца, бо воркер, асноўнае асяроддзе выканання і каталог traineddata павінны адпавядаць адно аднаму. Для цалкам аўтаномнага PDF з магчымасцю пошуку таксама задайце `VITE_OCR_FONT_BASE_URL`, каб шрыфты тэкставага слоя OCR загружаліся з вашага ўнутранага сервера, а не з публічных URL шрыфтоў Noto.
|
||||
|
||||
`VITE_DEFAULT_LANGUAGE` задае мову інтэрфейсу для новых наведвальнікаў. Падтрымліваемыя значэнні: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Карыстальнікі ўсё роўна могуць пераключаць мову, гэта змяняе толькі значэнне па змаўчанні.
|
||||
|
||||
Прыклад:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Уласны брэндынг
|
||||
|
||||
Замяніце лагатып, назву і тэкст футэра BentoPDF па змаўчанні на свае. Змясціце файл лагатыпа ў тэчку `public/` (або выкарыстайце існуючы відарыс), затым перадайце зменныя брэндынгу падчас зборкі:
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
Брэндынг працуе і ў поўным рэжыме, і ў Simple Mode, і можа спалучацца з усімі іншымі параметрамі зборкі.
|
||||
|
||||
### Адключэнне асобных інструментаў
|
||||
|
||||
Хавайце інструменты з інтэрфейсу дзеля адпаведнасці патрабаванням або бяспекі. Адключаныя інструменты выдаляюцца з галоўнай старонкі, вынікаў пошуку, спалучэнняў клавіш і канструктара працоўных працэсаў. Пры прамым пераходзе па URL паказваецца старонка "інструмент недаступны".
|
||||
|
||||
ID інструмента гэта URL старонкі без `.html`. Напрыклад, калі інструмент знаходзіцца па адрасе `bentopdf.com/edit-pdf.html`, яго ID `edit-pdf`.
|
||||
|
||||
#### Як знайсці ID інструментаў
|
||||
|
||||
Самы просты спосаб: адкрыйце любы інструмент у BentoPDF і паглядзіце на URL. Апошняя частка шляху (без `.html`) і ёсць ID інструмента.
|
||||
|
||||
<details>
|
||||
<summary>Поўны спіс ID інструментаў</summary>
|
||||
|
||||
**Рэдагаванне і анатацыі:** `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`
|
||||
|
||||
**Канвертаваць у 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`
|
||||
|
||||
**Канвертаваць з 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`
|
||||
|
||||
**Арганізацыя і кіраванне:** `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`
|
||||
|
||||
**Аптымізацыя і аднаўленне:** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Бяспека:** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Варыянт 1: Падчас зборкі (Docker build arg)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
Спіс адключаных інструментаў убудоўваецца ў JavaScript-бандл. Для змены патрэбна перазборка.
|
||||
|
||||
#### Варыянт 2: Падчас выканання (config.json)
|
||||
|
||||
Падключыце файл `config.json` у каталог, які аддаецца серверам, перазборка не патрэбна:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Або з 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
|
||||
```
|
||||
|
||||
Абодва спосабы можна спалучаць: спісы аб'ядноўваюцца. Калі інструмент ёсць хоць у адным, ён адключаны.
|
||||
|
||||
#### Адключэнне функцый рэдактара
|
||||
|
||||
Вы таксама можаце адключыць асобныя функцыі ў Рэдактары PDF (напрыклад, рэдагаванне канфідэнцыйных даных, анатацыі, формы), не адключаючы ўвесь інструмент. Дадайце `editorDisabledCategories` у ваш `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"editorDisabledCategories": ["redaction", "annotation-stamp"]
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Поўны спіс катэгорый рэдактара</summary>
|
||||
|
||||
**Маштаб:** `zoom`, `zoom-in`, `zoom-out`, `zoom-fit-page`, `zoom-fit-width`, `zoom-marquee`, `zoom-level`
|
||||
|
||||
**Анатацыі:** `annotation`, `annotation-markup`, `annotation-highlight`, `annotation-underline`, `annotation-strikeout`, `annotation-squiggly`, `annotation-ink`, `annotation-text`, `annotation-stamp`
|
||||
|
||||
**Фігуры:** `annotation-shape`, `annotation-rectangle`, `annotation-circle`, `annotation-line`, `annotation-arrow`, `annotation-polygon`, `annotation-polyline`
|
||||
|
||||
**Формы:** `form`, `form-textfield`, `form-checkbox`, `form-radio`, `form-select`, `form-listbox`, `form-fill-mode`
|
||||
|
||||
**Рэдагаванне даных:** `redaction`, `redaction-area`, `redaction-text`, `redaction-apply`, `redaction-clear`
|
||||
|
||||
**Дакумент:** `document`, `document-open`, `document-close`, `document-print`, `document-capture`, `document-export`, `document-fullscreen`, `document-protect`
|
||||
|
||||
**Старонка:** `page`, `spread`, `rotate`, `scroll`, `navigation`
|
||||
|
||||
**Панэль:** `panel`, `panel-sidebar`, `panel-search`, `panel-comment`
|
||||
|
||||
**Інструменты:** `tools`, `pan`, `pointer`, `capture`
|
||||
|
||||
**Вылучэнне:** `selection`, `selection-copy`
|
||||
|
||||
**Гісторыя:** `history`, `history-undo`, `history-redo`
|
||||
|
||||
</details>
|
||||
|
||||
Катэгорыі іерархічныя: адключэнне бацькоўскай (напрыклад, `annotation`) адключае ўсе даччыныя.
|
||||
|
||||
### Адключэнне лічыльніка зорак GitHub
|
||||
|
||||
Панэль навігацыі паказвае актуальную колькасць зорак GitHub, якая загружаецца з `api.github.com` пры загрузцы старонкі. Гэта адзіны знешні запыт, не звязаны з функцыямі PDF. Адключыце яго падчас зборкі, каб выдаліць код запыту з бандла і прыбраць `api.github.com` з Content-Security-Policy:
|
||||
|
||||
```bash
|
||||
docker build --build-arg DISABLE_GITHUB_STARS=true -t bentopdf .
|
||||
```
|
||||
|
||||
Зборкі Simple Mode (`bentopdf-simple`) заўсёды прапускаюць лічыльнік зорак; гэты сцяг патрэбны толькі для Commercial build або ўласных поўных зборак.
|
||||
|
||||
### Уласныя URL WASM (ізаляваныя / уласныя разгортванні)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> URL WASM убудоўваюцца ў JavaScript **падчас зборкі**. Файлы WASM спампоўвае **браўзер карыстальніка** падчас выканання, Docker не спампоўвае іх падчас зборкі. Для ізаляваных сетак вы павінны размясціць файлы WASM на ўнутраным серверы, даступным для браўзераў.
|
||||
|
||||
**Поўны працэс для ізаляванай сеткі:**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Для `--ocr-languages` выкарыстоўвайце коды, якія выводзіць `bash scripts/prepare-airgap.sh --list-ocr-languages`, або шукайце па назве з `bash scripts/prepare-airgap.sh --search-ocr-language <term>`. Калі збіраеце з абмежаваным наборам моў OCR, перадайце тыя ж коды ў `VITE_TESSERACT_AVAILABLE_LANGUAGES`, каб праграма паказвала толькі ўключаныя мовы. Для цалкам аўтаномнага OCR таксама размясціце ўбудаваны каталог `ocr-fonts/` і ўкажыце на яго ў `VITE_OCR_FONT_BASE_URL`.
|
||||
|
||||
Задайце зменнай пусты радок, каб адключыць адпаведны модуль (карыстальнікам давядзецца наладзіць яго ўручную ў пашыраных наладах).
|
||||
|
||||
## Уласны порт
|
||||
|
||||
Па змаўчанні BentoPDF слухае порт `8080` унутры кантэйнера. Каб змяніць яго, задайце зменную асяроддзя `PORT` пры запуску:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:9090 -e PORT=9090 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
| Зменная | Апісанне | Па змаўчанні |
|
||||
| ------- | ----------------------- | ------------ |
|
||||
| `PORT` | Порт Nginx у кантэйнеры | `8080` |
|
||||
|
||||
Гэта працуе і са стандартным, і з nonroot Dockerfile.
|
||||
|
||||
## Уласны ID карыстальніка (PUID/PGID)
|
||||
|
||||
Для асяроддзяў, дзе патрабуецца запуск ад пэўнага карыстальніка без правоў root (NAS-прылады, Kubernetes з security contexts, палітыкі арганізацыі), BentoPDF мае асобны Dockerfile з падтрымкай PUID/PGID у стылі LSIO.
|
||||
|
||||
### Зборка і запуск
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Зменныя асяроддзя
|
||||
|
||||
| Зменная | Апісанне | Па змаўчанні |
|
||||
| -------------- | ----------------------------- | ------------ |
|
||||
| `PUID` | ID карыстальніка для запуску | `1000` |
|
||||
| `PGID` | ID групы для запуску | `1000` |
|
||||
| `PORT` | Порт Nginx | `8080` |
|
||||
| `DISABLE_IPV6` | Адключыць праслухоўванне 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
|
||||
```
|
||||
|
||||
### Як гэта працуе
|
||||
|
||||
Кантэйнер запускаецца ад root, стварае карыстальніка з зададзенымі PUID/PGID, змяняе ўладальніка ўсіх каталогаў з правам запісу, затым скідае прывілеі з дапамогай `su-exec`. Працэс nginx цалкам працуе ад указанага вамі карыстальніка.
|
||||
|
||||
> [!NOTE]
|
||||
> Стандартны `Dockerfile` выкарыстоўвае `nginx-unprivileged` (UID 101) і рэкамендуецца для большасці разгортванняў. Выкарыстоўвайце `Dockerfile.nonroot`, толькі калі вам патрэбны пэўны UID/GID.
|
||||
|
||||
> [!WARNING]
|
||||
> PUID/PGID не можа быць `0` (root). Entrypoint правярае ўваходныя даныя і завяршаецца з памылкай пры няправільных значэннях.
|
||||
|
||||
## З Traefik (зваротны проксі)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## З Caddy (зваротны проксі)
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
## Ліміты рэсурсаў
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
```
|
||||
|
||||
## Podman Quadlet (інтэграцыя з systemd)
|
||||
|
||||
[Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) дазваляе запускаць кантэйнеры Podman як сэрвісы systemd. Гэта ідэальна для працоўных разгортванняў на Linux.
|
||||
|
||||
### Базавая наладка Quadlet
|
||||
|
||||
Стварыце файл юніта кантэйнера ў `~/.config/containers/systemd/bentopdf.container` (карыстальнік) або `/etc/containers/systemd/bentopdf.container` (сістэма):
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Уключэнне і запуск
|
||||
|
||||
```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]
|
||||
> Для агульнасістэмнага разгортвання выкарыстоўвайце `systemctl` без сцяга `--user` і змясціце файл у `/etc/containers/systemd/`.
|
||||
|
||||
### Quadlet з праверкай стану
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Аўтаабнаўленне з Quadlet
|
||||
|
||||
Podman можа аўтаматычна абнаўляць кантэйнеры, калі з'яўляюцца новыя вобразы:
|
||||
|
||||
```bash
|
||||
# Enable auto-update timer
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
|
||||
# Check for updates manually
|
||||
podman auto-update
|
||||
|
||||
# Dry run (check without updating)
|
||||
podman auto-update --dry-run
|
||||
```
|
||||
|
||||
### Сеткавая канфігурацыя Quadlet
|
||||
|
||||
Для ўласнай сеткавай канфігурацыі стварыце сеткавы файл `bentopdf.network`:
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Subnet=10.89.0.0/24
|
||||
Gateway=10.89.0.1
|
||||
```
|
||||
|
||||
Затым спашліцеся на яго ў файле кантэйнера:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ContainerName=bentopdf
|
||||
PublishPort=3000:8080
|
||||
Network=bentopdf.network
|
||||
```
|
||||
|
||||
## Абнаўленне
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Recreate container
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Разгортванне на Hostinger
|
||||
|
||||
[Hostinger](https://hostinger.com) папулярны правайдар віртуальнага хостынгу. У гэтым кіраўніцтве апісана разгортванне BentoPDF на віртуальным хостынгу Hostinger.
|
||||
|
||||
## Патрабаванні
|
||||
|
||||
- Тарыф хостынгу Hostinger з доступам да файлавага менеджара
|
||||
- Node.js, усталяваны лакальна для зборкі
|
||||
|
||||
## Крок 1: Збярыце праект
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Сабраныя файлы будуць у тэчцы `dist`.
|
||||
|
||||
## Крок 2: Загрузка ў Hostinger
|
||||
|
||||
### Разгортванне ў каранёвым дамене
|
||||
|
||||
1. Увайдзіце ў ваш уліковы запіс Hostinger
|
||||
2. Перайдзіце ў **File Manager** → **public_html**
|
||||
3. **Выдаліце** ўсе існуючыя файлы (пры неабходнасці зрабіце рэзервовую копію)
|
||||
4. **Загрузіце** ўвесь змест вашай лакальнай тэчкі `dist` у `public_html`
|
||||
|
||||
### Разгортванне ў падкаталогу
|
||||
|
||||
Калі разгортваеце ў падкаталог (напрыклад, `yourdomain.com/pdf-tools/`):
|
||||
|
||||
1. Збярыце з правільным базавым URL:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
2. Стварыце тэчку ў Hostinger:
|
||||
- Перайдзіце ў **File Manager** → **public_html**
|
||||
- Стварыце новую тэчку: `pdf-tools`
|
||||
3. Загрузіце ўвесь змест `dist` у `public_html/pdf-tools/`
|
||||
|
||||
## Крок 3: Стварыце файл .htaccess
|
||||
|
||||
Стварыце файл `.htaccess` у корані тэчкі разгортвання (`public_html` або `public_html/pdf-tools/`):
|
||||
|
||||
::: warning Важна
|
||||
Для разгортвання ў падкаталогу змяніце `RewriteBase /` на `RewriteBase /pdf-tools/` (або назву вашай тэчкі).
|
||||
:::
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Прыклад .htaccess для падкаталога
|
||||
|
||||
Для `yourdomain.com/pdf-tools/` абнавіце гэтыя радкі:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Вырашэнне праблем
|
||||
|
||||
### Файлы WASM не загружаюцца
|
||||
|
||||
Пераканайцеся, што MIME-тыпы правільна зададзены ў `.htaccess`:
|
||||
|
||||
```apache
|
||||
AddType application/wasm .wasm
|
||||
```
|
||||
|
||||
### Інструменты LibreOffice не працуюць
|
||||
|
||||
Загалоўкі бяспекі крытычна важныя для SharedArrayBuffer:
|
||||
|
||||
```apache
|
||||
Header always set Cross-Origin-Opener-Policy "same-origin"
|
||||
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
||||
```
|
||||
|
||||
Калі загалоўкі не прымяняюцца, звярніцеся ў падтрымку Hostinger, каб уключыць `mod_headers`.
|
||||
|
||||
### Памылкі 404 пры абнаўленні старонкі
|
||||
|
||||
Пераканайцеся, што правіла пашырэння `.html` і моўныя маршруты настроены правільна. BentoPDF выкарыстоўвае статычныя HTML-файлы, а не 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]
|
||||
```
|
||||
|
||||
### Ліміты загрузкі файлаў
|
||||
|
||||
У Hostinger могуць быць ліміты памеру файлаў. Калі масавая загрузка не ўдаецца, загружайце файлы WASM асобна.
|
||||
@@ -0,0 +1,538 @@
|
||||
# Кіраўніцтва па самастойным размяшчэнні
|
||||
|
||||
BentoPDF можна размясціць на ўласнай інфраструктуры. Гэта кіраўніцтва апісвае розныя варыянты разгортвання.
|
||||
|
||||
## Хуткі старт з Docker / Podman
|
||||
|
||||
Самы хуткі спосаб размясціць BentoPDF самастойна:
|
||||
|
||||
> [!TIP]
|
||||
> BentoPDF пастаўляецца ў дзвюх зборках:
|
||||
>
|
||||
> - **Зборка для самастойнага размяшчэння**: `ghcr.io/alam00000/bentopdf-simple:latest`. Усе інструменты PDF, **без** маркетынгавага афармлення (без hero-блока, FAQ, водгукаў, ніжняга калантытула). Выкарыстоўвайце яе для ўнутраных разгортванняў каманды або арганізацыі. Гэта **не** скарочаная "lite" версія.
|
||||
> - **Камерцыйная зборка**: `ghcr.io/alam00000/bentopdf:latest`. Поўны маркетынгавы сайт, які выкарыстоўвае сам bentopdf.com і ўладальнікі камерцыйнай ліцэнзіі для публічных разгортванняў.
|
||||
>
|
||||
> Калі сумняваецеся, бярыце зборку для самастойнага размяшчэння.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Пераўтварэнне офісных файлаў патрабуе `SharedArrayBuffer`, а значыць, праграма павінна быць ізаляванай паміж крыніцамі (cross-origin isolated) і раздавацца з бяспечнага кантэксту. Афіцыйны вобраз ужо адпраўляе патрэбныя загалоўкі COOP/COEP, але браўзеры ўсё роўна адключаюць `SharedArrayBuffer` на звычайных HTTP-адрасах лакальнай сеткі, напрыклад `http://192.168.x.x`.
|
||||
>
|
||||
> Выкарыстоўвайце `http://localhost` толькі для праверкі на той жа прыладзе. Калі карыстальнікі заходзяць у BentoPDF праз IP або імя хаста ў LAN, падключыце 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
|
||||
```
|
||||
|
||||
Або з Docker Compose / Podman Compose:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||
ports:
|
||||
- '3000:8080'
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Podman Compose
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
## Podman Quadlet (Linux Systemd)
|
||||
|
||||
Запускайце BentoPDF як сэрвіс systemd. Стварыце `~/.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
|
||||
```
|
||||
|
||||
Поўная дакументацыя па Quadlet ёсць у [кіраўніцтве па разгортванні з Docker](/be/self-hosting/docker).
|
||||
|
||||
## Зборка з зыходнага кода
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Параметры канфігурацыі
|
||||
|
||||
### Зборка для самастойнага размяшчэння (Simple Mode)
|
||||
|
||||
Зборка для самастойнага размяшчэння (вобраз `bentopdf-simple`, таксама вядомы як Simple Mode) **функцыянальна ідэнтычная** камерцыйнай зборцы: усе інструменты PDF прысутнічаюць і працуюць аднолькава. Яна толькі хавае маркетынгавае афармленне, якое мае сэнс толькі на самім bentopdf.com або ў камерцыйным публічным разгортванні. **Гэта не скарочаная і не "lite" версія.**
|
||||
|
||||
**Што хавае зборка для самастойнага размяшчэння** (толькі касметыка, ніякія функцыі PDF не выдаляюцца):
|
||||
|
||||
- Панэль навігацыі, hero-блок, раздзел магчымасцей, FAQ, водгукі, ніжні калантытул
|
||||
- Змяняе загаловак старонкі на "Інструменты PDF"
|
||||
|
||||
**Што застаецца ў зборцы для самастойнага размяшчэння** (усё, што сапраўды працуе з PDF):
|
||||
|
||||
- Усе інструменты PDF (аб'яднанне, падзел, рэдагаванне, подпіс, OCR, пераўтварэнне офісных файлаў і г.д.)
|
||||
- Падтрымка ўласнага брэндынгу, уся канфігурацыя падчас зборкі і выканання
|
||||
|
||||
Камерцыйная зборка (`ghcr.io/alam00000/bentopdf:latest`) ляжыць у аснове самога bentopdf.com і выкарыстоўваецца ўладальнікамі камерцыйнай ліцэнзіі для публічных разгортванняў: яна дадае hero-блок, FAQ, водгукі і ніжні калантытул, якія не мелі б сэнсу ва ўнутраным інструменце.
|
||||
|
||||
Калі вы размяшчаеце BentoPDF для сваёй каманды, арганізацыі або як унутраны інструмент, бярыце зборку для самастойнага размяшчэння:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Падрабязнасці глядзіце ў [SIMPLE_MODE.md](https://github.com/alam00000/bentopdf/blob/main/SIMPLE_MODE.md).
|
||||
|
||||
### Камерцыйная зборка (публічныя разгортванні пад уласным брэндам)
|
||||
|
||||
Камерцыйная зборка (вобраз `bentopdf`, без суфікса `-simple`) ляжыць у аснове самога bentopdf.com. Яна ўключае поўны маркетынгавы сайт (hero-блок, магчымасці, FAQ, водгукі, ніжні калантытул) у дадатак да ўсіх інструментаў PDF. Выкарыстоўвайце гэтую зборку, калі хочаце **разгарнуць BentoPDF як публічны PDF-сэрвіс пад уласным брэндам**, напрыклад:
|
||||
|
||||
- Вы прапануеце BentoPDF як SaaS для канчатковых карыстальнікаў на сваім дамене
|
||||
- Вам патрэбна поўная старонка (маркетынгавыя раздзелы + інструменты), а не толькі голы набор інструментаў
|
||||
- Вы ўладальнік камерцыйнай ліцэнзіі і ўбудоўваеце BentoPDF у камерцыйны прадукт
|
||||
|
||||
**Запуск як ёсць** (брэндынг BentoPDF, зручна для папярэдняга прагляду зборкі):
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
**Зборка з уласным брэндам** (тыповы камерцыйны сцэнарый):
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Усе іншыя параметры зборкі (`BASE_URL`, `VITE_DEFAULT_LANGUAGE`, `DISABLE_TOOLS`, перавызначэнні URL для WASM і г.д.) працуюць гэтак жа, як і ў зборцы для самастойнага размяшчэння.
|
||||
|
||||
::: warning Ліцэнзаванне
|
||||
Выкарыстанне камерцыйнай зборкі дазволена паводле любога з двух варыянтаў ліцэнзіі BentoPDF:
|
||||
|
||||
- **AGPL-3.0** (бясплатна): дазволена, калі ваша разгортванне публікуе ўвесь свой зыходны код пад AGPL, уключаючы змены брэндынгу і звязаную бізнес-логіку.
|
||||
- **Камерцыйная ліцэнзія** ($79 пажыццёва): патрэбна для закрытых / прапрыетарных разгортванняў, калі вы не адкрываеце код свайго форка з брэндынгам або бізнес-код.
|
||||
|
||||
Поўнае параўнанне глядзіце на [старонцы ліцэнзавання](https://www.bentopdf.com/licensing). Модулі WASM пад AGPL (PyMuPDF, Ghostscript, CoherentPDF) загружаюцца з CDN падчас выканання, таму яны не трапляюць у ваш вобраз і не змяняюць вашага ліцэнзійнага становішча.
|
||||
:::
|
||||
|
||||
### Базавы URL
|
||||
|
||||
Разгортванне ў падкаталог:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf-tools/ npm run build
|
||||
```
|
||||
|
||||
### Уласны брэндынг
|
||||
|
||||
Замяніце стандартны лагатып, назву і тэкст ніжняга калантытула BentoPDF на свае падчас зборкі:
|
||||
|
||||
| Зменная | Апісанне | Па змаўчанні |
|
||||
| ------------------ | ------------------------------------------------- | --------------------------------------- |
|
||||
| `VITE_BRAND_NAME` | Назва брэнда ў загалоўку і ніжнім калантытуле | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Шлях да лагатыпа адносна `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Уласны тэкст ніжняга калантытула/аўтарскага права | `© 2026 BentoPDF. All rights reserved.` |
|
||||
|
||||
```bash
|
||||
# Place your logo in public/, then build
|
||||
VITE_BRAND_NAME="AcmePDF" \
|
||||
VITE_BRAND_LOGO="images/acme-logo.svg" \
|
||||
VITE_FOOTER_TEXT="© 2026 Acme Corp. Internal use only." \
|
||||
npm run build
|
||||
```
|
||||
|
||||
Або праз 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 .
|
||||
```
|
||||
|
||||
Брэндынг працуе і ў поўным рэжыме, і ў Simple Mode, і можа спалучацца з усімі іншымі параметрамі зборкі (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Адключэнне пэўных інструментаў
|
||||
|
||||
Хавайце асобныя інструменты з меркаванняў адпаведнасці патрабаванням або бяспекі. Адключаныя інструменты выдаляюцца з галоўнай старонкі, пошуку, спалучэнняў клавіш, канструктара працоўных працэсаў і недаступныя па прамым URL.
|
||||
|
||||
Ідэнтыфікатары інструментаў: гэта URL старонкі без `.html`. Адкрыйце любы інструмент і паглядзіце на URL (напрыклад, `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Падчас зборкі** (убудоўваецца ў бандл):
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**Падчас выканання** (без перазборкі, падключыце `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
|
||||
```
|
||||
|
||||
Абодва спосабы можна спалучаць: спісы аб'ядноўваюцца.
|
||||
|
||||
Таксама можна адключыць асобныя функцыі ў рэдактары PDF (напрыклад, рэдагаванне канфідэнцыйных даных), не адключаючы ўвесь інструмент, дадаўшы `editorDisabledCategories` у `config.json`. Поўны спіс катэгорый глядзіце ў [кіраўніцтве па Docker](/be/self-hosting/docker#адключэнне-функцыи-рэдактара).
|
||||
|
||||
## Кіраўніцтвы па разгортванні
|
||||
|
||||
Выберыце платформу:
|
||||
|
||||
- [Vercel](/be/self-hosting/vercel)
|
||||
- [Netlify](/be/self-hosting/netlify)
|
||||
- [Cloudflare Pages](/be/self-hosting/cloudflare)
|
||||
- [AWS S3 + CloudFront](/be/self-hosting/aws)
|
||||
- [Hostinger](/be/self-hosting/hostinger)
|
||||
- [Nginx](/be/self-hosting/nginx)
|
||||
- [Apache](/be/self-hosting/apache)
|
||||
- [Docker](/be/self-hosting/docker)
|
||||
- [Kubernetes](/be/self-hosting/kubernetes)
|
||||
- [CORS-проксі](/be/self-hosting/cors-proxy) - патрэбны для лічбавых подпісаў
|
||||
|
||||
## Тыповыя праблемы
|
||||
|
||||
### Word / ODT / Excel / PowerPoint у PDF завісае (SharedArrayBuffer недаступны)
|
||||
|
||||
**Сімптом**: пераўтварэнні дакументаў на базе LibreOffice (Word, ODT, Excel, PowerPoint у PDF) завісаюць на ~55% або не пачынаюцца. У кансолі браўзера можа быць:
|
||||
|
||||
> `ReferenceError: SharedArrayBuffer is not defined`
|
||||
|
||||
…або `window.crossOriginIsolated` вяртае `false`, або кампіляцыя WASM паведамляе `expected magic word 00 61 73 6d, found 1f 8b 08 08`.
|
||||
|
||||
**Прычына**: LibreOffice WASM патрабуе `SharedArrayBuffer`, які браўзер уключае толькі калі старонка **ізаляваная паміж крыніцамі** І раздаецца з **бяспечнага кантэксту**. Гэта значыць, што павінны выконвацца дзве ўмовы:
|
||||
|
||||
1. Кожны адказ уключае абодва загалоўкі:
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
2. Старонка раздаецца з `https://...` або `http://localhost`. Звычайны HTTP на IP лакальнай сеткі (напрыклад, `http://192.168.x.x`) НЕ лічыцца бяспечным: браўзеры адключаюць там `SharedArrayBuffer`.
|
||||
|
||||
Неадпаведнасць `00 61 73 6d / 1f 8b 08 08` з'яўляецца асобным сімптомам: у папярэдне сціснутых файлаў `.wasm.gz` / `.data.gz` няма загалоўка адказу `Content-Encoding: gzip`, таму браўзер атрымлівае сырыя байты gzip замест распакаванага WASM.
|
||||
|
||||
**Рашэнне**: дакладную канфігурацыю глядзіце ў кіраўніцтве для вашай платформы:
|
||||
|
||||
- [Nginx →](/be/self-hosting/nginx#word-odt-excel-у-pdf-не-працуе)
|
||||
- [Apache →](/be/self-hosting/apache#word-odt-excel-у-pdf-не-працуе)
|
||||
- [AWS S3 + CloudFront →](/be/self-hosting/aws#крок-3b-палітыка-загалоукау-адказу-абавязкова-для-libreoffice-wasm)
|
||||
- [Cloudflare Pages →](/be/self-hosting/cloudflare#фаил-канфігурацыі) (файл `_headers`)
|
||||
- [Netlify →](/be/self-hosting/netlify#word-odt-excel-у-pdf-спыняецца-на-55) (`netlify.toml`)
|
||||
- [Vercel →](/be/self-hosting/vercel#word-odt-excel-у-pdf-не-працуе) (`vercel.json`)
|
||||
- [Hostinger →](/be/self-hosting/hostinger#інструменты-libreoffice-не-працуюць) (`.htaccess`)
|
||||
- [Kubernetes →](/be/self-hosting/kubernetes#як-пераканацца-што-загалоукі-sharedarraybuffer-працуюць-ingress-gateway)
|
||||
- **Docker**: апрацоўваецца аўтаматычна ўбудаванай канфігурацыяй nginx, нічога рабіць не трэба.
|
||||
|
||||
**Праверка**: адкрыйце кансоль DevTools на любой старонцы BentoPDF і выканайце:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Калі старонка працуе праз HTTPS або `http://localhost` І абодва загалоўкі COEP/COOP ёсць у кожным адказе, абедзве праверкі праходзяць. Калі вы на `http://192.168.x.x` або іншым не-loopback HTTP-адрасе, падключыце HTTPS: выпраўлення толькі загалоўкамі няма.
|
||||
|
||||
### Файлы `.mjs` аддаюцца як `application/octet-stream`
|
||||
|
||||
**Сімптом**: Падпісаць PDF / Запаўненне форм / некаторыя іншыя інструменты паказваюць пусты праглядальнік або не загружаюцца. Кансоль браўзера паведамляе:
|
||||
|
||||
> `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.`
|
||||
|
||||
**Прычына**: у вашым вэб-серверы або зваротным проксі няма адпаведнасці MIME-тыпу для файлаў `.mjs` (убудаваны праглядальнік PDF пастаўляецца з ES-модулямі з гэтым пашырэннем). Многія стандартныя канфігурацыі сервераў па змаўчанні аддаюць `application/octet-stream` для невядомых пашырэнняў, а браўзеры адмаўляюцца выконваць такое як ES-модулі.
|
||||
|
||||
**Рашэнне**: дакладны фрагмент глядзіце ў кіраўніцтве для вашай платформы:
|
||||
|
||||
- [Nginx →](/be/self-hosting/nginx#падпісаць-pdf-або-запауненне-форм-паказвае-пусты-праглядальнік-памылка-mime-mjs)
|
||||
- [Apache →](/be/self-hosting/apache#падпісаць-pdf-або-запоуніць-форму-pdf-паказвае-пусты-прагляднік-памылка-mime-mjs)
|
||||
- [AWS S3 + CloudFront →](/be/self-hosting/aws#крок-2-зборка-і-загрузка) (глядзіце `aws s3 cp ... --include "*.mjs"`)
|
||||
- [Kubernetes →](/be/self-hosting/kubernetes#памылкі-mime-тыпу-mjs-пусты-iframe-у-падпісаць-pdf-запауненне-форм)
|
||||
- **Docker, Vercel, Netlify, Cloudflare Pages, Hostinger**: апрацоўваецца аўтаматычна, нічога рабіць не трэба.
|
||||
|
||||
**Праверка**: адкрыйце DevTools → укладка Network, знайдзіце няўдалы запыт `.mjs` і праверце загаловак адказу `Content-Type`. Ён павінен быць `application/javascript`. Калі ён усё яшчэ `application/octet-stream`, знешні зваротны проксі або CDN можа паўторна вызначаць тып: праверце кожны ўзровень ланцужка раздачы.
|
||||
|
||||
## Канфігурацыя WASM (кампаненты AGPL)
|
||||
|
||||
BentoPDF **не ўключае** ў свой зыходны код бібліятэкі апрацоўкі пад ліцэнзіяй AGPL, але **загадзя наладжвае URL CDN**, каб усе функцыі працавалі адразу, без ручной налады.
|
||||
|
||||
::: tip Без налад па змаўчанні
|
||||
Пачынаючы з v2.0.0, модулі WASM загадзя наладжаны на загрузку з CDN jsDelivr праз зменныя асяроддзя. Усе пашыраныя функцыі працуюць адразу без налад з боку карыстальніка.
|
||||
:::
|
||||
|
||||
| Кампанент | Ліцэнзія | Функцыі |
|
||||
| --------------- | -------- | ------------------------------------------------------------------- |
|
||||
| **PyMuPDF** | AGPL-3.0 | Пераўтварэнне EPUB/MOBI/FB2/XPS, выманне выяў, выманне табліц |
|
||||
| **Ghostscript** | AGPL-3.0 | Пераўтварэнне ў PDF/A, сцісканне, выраўноўванне нахілу, растраванне |
|
||||
| **CoherentPDF** | AGPL-3.0 | Змест, далучэнні, аб'яднанне PDF з закладкамі |
|
||||
|
||||
### Зменныя асяроддзя па змаўчанні
|
||||
|
||||
Яны задаюцца ў `.env.production` і ўбудоўваюцца ў зборку:
|
||||
|
||||
```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=
|
||||
```
|
||||
|
||||
### Перавызначэнне URL для WASM
|
||||
|
||||
Значэнні па змаўчанні можна перавызначыць падчас зборкі для ўласных разгортванняў:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Каб цалкам адключыць модуль (патрабуючы ручной налады карыстальнікам праз пашыраныя налады), задайце яго зменнай пусты радок.
|
||||
|
||||
Для OCR альбо пакіньце ўсе зменныя `VITE_TESSERACT_*` пустымі і выкарыстоўвайце стандартныя анлайн-рэсурсы, альбо задайце URL worker/core/lang разам для ўласнага або аўтаномнага OCR. Калі вы ўключаеце толькі пэўныя мовы OCR, таксама задайце `VITE_TESSERACT_AVAILABLE_LANGUAGES` з тымі ж кодамі праз коску, каб інтэрфейс прапаноўваў толькі ўсталяваныя мовы, а непадтрымліваемы выбар завяршаўся зразумелай памылкай. Для поўнасцю аўтаномнага выніку PDF з пошукам таксама задайце `VITE_OCR_FONT_BASE_URL` на ўнутраны каталог, які раздае ўбудаваныя шрыфты OCR.
|
||||
|
||||
Карыстальнікі таксама могуць у любы момант перавызначыць гэтыя значэнні праз **Пашыраныя налады** ў інтэрфейсе: перавызначэнні карыстальніка, захаваныя ў браўзеры, маюць прыярытэт над значэннямі асяроддзя.
|
||||
|
||||
### Ізаляванае / аўтаномнае разгортванне
|
||||
|
||||
Для сетак без доступу да інтэрнэту (дзяржаўныя, медыцынскія, фінансавыя і г.д.). URL для WASM убудоўваюцца ў JavaScript **падчас зборкі**, а самі файлы WASM спампоўвае **браўзер карыстальніка** падчас выканання. Таму ўсё трэба падрыхтаваць на машыне з інтэрнэтам, а потым перанесці ў ізаляваную сетку.
|
||||
|
||||
#### Аўтаматычны скрыпт (рэкамендуецца)
|
||||
|
||||
Уключаны скрыпт `prepare-airgap.sh` аўтаматызуе ўвесь працэс: спампоўвае пакеты WASM, збірае вобраз Docker і стварае самадастатковы пакет са скрыптом усталявання.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
У выніку атрымаецца каталог пакета:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Перанясіце пакет у ізаляваную сетку праз USB, унутраны рэпазіторый артэфактаў або іншы дазволены спосаб. Затым запусціце ўключаны скрыпт усталявання:
|
||||
|
||||
```bash
|
||||
cd bentopdf-airgap-bundle
|
||||
bash setup.sh
|
||||
```
|
||||
|
||||
Скрыпт загружае вобраз Docker, распакоўвае файлы WASM і пры жаданні запускае кантэйнер.
|
||||
|
||||
**Параметры скрыпта:**
|
||||
|
||||
| Сцяг | Апісанне | Па змаўчанні |
|
||||
| ------------------------------ | ---------------------------------------------------------- | -------------------------------------- |
|
||||
| `--wasm-base-url <url>` | Дзе WASM будуць размешчаны ўнутры сеткі | _(абавязкова, запытваецца, калі няма)_ |
|
||||
| `--image-name <name>` | Тэг вобраза Docker | `bentopdf` |
|
||||
| `--output-dir <path>` | Каталог выніковага пакета | `./bentopdf-airgap-bundle` |
|
||||
| `--simple-mode` | Уключыць Simple Mode | выкл. |
|
||||
| `--base-url <path>` | Базавы URL падкаталога (напрыклад, `/pdf/`) | `/` |
|
||||
| `--language <code>` | Мова інтэрфейсу па змаўчанні (напрыклад, `fr`, `de`) | _(няма)_ |
|
||||
| `--brand-name <name>` | Уласная назва брэнда | _(няма)_ |
|
||||
| `--brand-logo <path>` | Шлях да лагатыпа адносна `public/` | _(няма)_ |
|
||||
| `--footer-text <text>` | Уласны тэкст ніжняга калантытула | _(няма)_ |
|
||||
| `--ocr-languages <list>` | Мовы OCR для ўключэння ў пакет праз коску | `eng` |
|
||||
| `--list-ocr-languages` | Вывесці падтрымліваемыя коды і назвы OCR і выйсці | выкл. |
|
||||
| `--search-ocr-language <term>` | Пошук кодаў OCR па назве або скарачэнні | выкл. |
|
||||
| `--dockerfile <path>` | Dockerfile для выкарыстання | `Dockerfile` |
|
||||
| `--skip-docker` | Прапусціць зборку і экспарт Docker | выкл. |
|
||||
| `--skip-wasm` | Прапусціць спампоўванне WASM (выкарыстаць існуючыя `.tgz`) | выкл. |
|
||||
|
||||
Інтэрактыўны рэжым таксама прымае `list`, каб вывесці поўны спіс падтрымліваемых кодаў Tesseract, і `search <term>` для пошуку адпаведнасцей, напрыклад `search german` або `search chi`.
|
||||
|
||||
::: warning Патрабаванне аднаго паходжання
|
||||
Файлы WASM павінны раздавацца з **таго ж паходжання (origin)**, што і праграма BentoPDF. Web Workers выкарыстоўваюць `importScripts()`, які не можа загружаць скрыпты з іншых паходжанняў. Напрыклад, калі BentoPDF працуе на `https://internal.example.com`, базавы URL для WASM таксама павінен быць `https://internal.example.com/wasm`.
|
||||
:::
|
||||
|
||||
#### Ручныя крокі
|
||||
|
||||
<details>
|
||||
<summary>Калі вы аддаеце перавагу рабіць усё ўручную без скрыпта</summary>
|
||||
|
||||
**Крок 1: Спампуйце пакеты WASM і OCR** (на машыне з інтэрнэтам)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
**Крок 2: Збярыце вобраз Docker з унутранымі URL**
|
||||
|
||||
```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 .
|
||||
```
|
||||
|
||||
**Крок 3: Экспартуйце вобраз Docker**
|
||||
|
||||
```bash
|
||||
docker save bentopdf -o bentopdf.tar
|
||||
```
|
||||
|
||||
**Крок 4: Перанясіце ў ізаляваную сетку**
|
||||
|
||||
Скапіруйце праз USB, унутраны рэпазіторый артэфактаў або іншы дазволены спосаб:
|
||||
|
||||
- `bentopdf.tar`: вобраз Docker
|
||||
- Пяць пакетаў WASM/OCR `.tgz` з кроку 1
|
||||
- Каталог `tesseract-langdata/` з кроку 1
|
||||
- Каталог `ocr-fonts/` з кроку 1
|
||||
|
||||
**Крок 5: Наладзьце ўнутры ізаляванай сеткі**
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Пераканайцеся, што файлы даступныя па URL, наладжаных на кроку 2, уключаючы `.../ocr/worker.min.js`, `.../ocr/core`, `.../ocr/lang-data` і `.../ocr/fonts`.
|
||||
|
||||
</details>
|
||||
|
||||
::: info Збіраеце з зыходнага кода замест Docker?
|
||||
Задайце зменныя ў `.env.production` перад запускам `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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Уласны проксі для WASM
|
||||
|
||||
Калі трэба раздаваць файлы WASM пад AGPL з правільнымі загалоўкамі CORS, можна разгарнуць просты проксі. Прыклад рэалізацыі ёсць у [кіраўніцтве па Cloudflare WASM Proxy](https://github.com/alam00000/bentopdf/blob/main/cloudflare/WASM-PROXY.md).
|
||||
|
||||
::: tip Чаму асобна?
|
||||
Такое раздзяленне забяспечвае:
|
||||
|
||||
- Зразумелую юрыдычную адпаведнасць для камерцыйных карыстальнікаў
|
||||
- Ядро BentoPDF застаецца пад двайной ліцэнзіяй (AGPL-3.0 / камерцыйная)
|
||||
- Файлы WASM загружаюцца падчас выканання, а не ўключаюцца ў зыходны код
|
||||
:::
|
||||
|
||||
## Сістэмныя патрабаванні
|
||||
|
||||
| Патрабаванне | Мінімум |
|
||||
| ------------ | ------------------------------- |
|
||||
| Сховішча | ~100 MB (ядро без модуляў AGPL) |
|
||||
| RAM | 512 MB |
|
||||
| CPU | Любы сучасны працэсар |
|
||||
|
||||
::: tip
|
||||
BentoPDF з'яўляецца статычным сайтам: ніякая база даных або бэкэнд-сервер не патрэбныя!
|
||||
:::
|
||||
@@ -0,0 +1,211 @@
|
||||
# Разгортванне з Kubernetes
|
||||
|
||||
Kubernetes можа быць залішнім для статычнага сайта, але выдатна падыходзіць, калі вы ўжо карыстаецеся Helm + GitOps як стандартам.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Абавязковыя загалоўкі для пераўтварэння офісных файлаў**
|
||||
>
|
||||
> Інструменты на базе LibreOffice (пераўтварэнне Word, Excel, PowerPoint) патрабуюць гэтыя HTTP-загалоўкі для падтрымкі `SharedArrayBuffer`:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> Афіцыйныя вобразы BentoPDF з nginx уключаюць гэтыя загалоўкі. У Kubernetes **кантролеры Ingress/Gateway таксама з'яўляюцца зваротнымі проксі**, таму пераканайцеся, што гэтыя загалоўкі захоўваюцца (або дадайце іх на краі).
|
||||
|
||||
## Патрабаванні
|
||||
|
||||
- Кластар Kubernetes
|
||||
- Helm v3
|
||||
- Вобраз BentoPDF з nginx (напрыклад, `ghcr.io/alam00000/bentopdf:<tag>`), які працуе на **порце 8080**
|
||||
|
||||
## Разгортванне з Helm
|
||||
|
||||
### Усталяванне з гэтага рэпазіторыя (лакальны chart)
|
||||
|
||||
```bash
|
||||
kubectl create namespace bentopdf
|
||||
|
||||
helm upgrade --install bentopdf /path/to/bentopdf/chart \
|
||||
--namespace bentopdf \
|
||||
--set image.repository=ghcr.io/alam00000/bentopdf \
|
||||
--set image.tag=latest
|
||||
```
|
||||
|
||||
### Усталяванне з GHCR (OCI chart)
|
||||
|
||||
Калі chart апублікаваны ў GHCR як артэфакт 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
|
||||
```
|
||||
|
||||
## Адкрыццё доступу
|
||||
|
||||
### Port-forward (хуткая праверка)
|
||||
|
||||
```bash
|
||||
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
|
||||
```
|
||||
|
||||
### Ingress (неабавязкова)
|
||||
|
||||
Уключыце Ingress (прыклад для nginx-ingress):
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: pdf.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Gateway API (неабавязкова)
|
||||
|
||||
Гэты chart падтрымлівае Gateway API `Gateway` + `HTTPRoute`.
|
||||
|
||||
Прыклад (аператар Cloudflare Gateway API):
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Як пераканацца, што загалоўкі SharedArrayBuffer працуюць (Ingress/Gateway)
|
||||
|
||||
### Што "павінна" адбывацца
|
||||
|
||||
Канфігурацыя nginx у BentoPDF задае патрэбныя загалоўкі адказу. Большасць кантролераў Ingress/Gateway **перадаюць загалоўкі адказу ад upstream без змен**.
|
||||
|
||||
### Што можа гэта зламаць
|
||||
|
||||
- Палітыка кантролера/краю, якая **перавызначае** або **выдаляе** загалоўкі адказу
|
||||
- Прамежкавае ПЗ для "security headers", якое задае іншыя значэнні COOP/COEP
|
||||
|
||||
### Як праверыць
|
||||
|
||||
Выканайце гэта для свайго публічнага адраса:
|
||||
|
||||
```bash
|
||||
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
|
||||
```
|
||||
|
||||
Вы павінны ўбачыць:
|
||||
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### Калі ваш кантролер Ingress не захоўвае іх
|
||||
|
||||
Дадайце загалоўкі на краі (залежыць ад кантролера). Прыклад для **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;
|
||||
```
|
||||
|
||||
## Памылкі MIME-тыпу `.mjs` (пусты iframe у Падпісаць PDF / Запаўненне форм)
|
||||
|
||||
Калі ў кансолі браўзера бачна `Failed to load module script: ... non-JavaScript MIME type "application/octet-stream"` для запыту `.mjs`, кантролер Ingress/Gateway перад nginx BentoPDF замяняе загаловак `Content-Type` ад upstream на `application/octet-stream`.
|
||||
|
||||
Nginx у вобразе BentoPDF ужо аддае `.mjs` як `application/javascript`. Рашэнне: забараніць кантролеру выдаляць або замяняць яго. Для nginx-ingress:
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
location ~* \.mjs$ {
|
||||
types {} default_type application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
Або выкарыстайце фільтр `ResponseHeaderModifier` у Gateway API, каб задаць `Content-Type: application/javascript` для шляхоў `.mjs`. Праверце ў DevTools → укладка Network: няўдалы раней запыт `.mjs` цяпер павінен паказваць `content-type: application/javascript`.
|
||||
|
||||
### Калі вы выкарыстоўваеце Gateway API і хочаце прымусова дадаць загалоўкі
|
||||
|
||||
Gateway API падтрымлівае фільтр `ResponseHeaderModifier`. Яго можна далучыць у `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
|
||||
```
|
||||
|
||||
Падтрымка пэўных фільтраў залежыць ад вашага кантролера Gateway; калі фільтр ігнаруецца, дадайце загалоўкі на ўзроўні краю/кантролера.
|
||||
|
||||
## Адключэнне пэўных інструментаў
|
||||
|
||||
Выкарыстайце ConfigMap, каб адключаць інструменты падчас выканання без перазборкі вобраза:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Падключыце яго ў каталог, які раздаецца:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Ідэнтыфікатары інструментаў: гэта URL старонкі без `.html`. Адкрыйце любы інструмент і паглядзіце на URL (напрыклад, `edit-pdf`, `merge-pdf`, `compress-pdf`). Адключаныя інструменты схаваныя з галоўнай старонкі, пошуку, спалучэнняў клавіш, канструктара працоўных працэсаў і недаступныя па прамым URL. Поўны спіс параметраў глядзіце ў [кіраўніцтве па Docker](/be/self-hosting/docker#адключэнне-асобных-інструментау).
|
||||
@@ -0,0 +1,139 @@
|
||||
# Разгортванне на Netlify
|
||||
|
||||
[Netlify](https://netlify.com) прапануе выдатны хостынг статычных сайтаў са шчодрым бясплатным тарыфам.
|
||||
|
||||
## Разгортванне адным клікам
|
||||
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Ручное разгортванне
|
||||
|
||||
### Крок 1: Падключыце рэпазіторый
|
||||
|
||||
1. Увайдзіце ў [Netlify](https://app.netlify.com)
|
||||
2. Націсніце "Add new site" → "Import an existing project"
|
||||
3. Падключыце свой уліковы запіс GitHub
|
||||
4. Выберыце свой форк BentoPDF
|
||||
|
||||
### Крок 2: Наладзьце параметры зборкі
|
||||
|
||||
| Налада | Значэнне |
|
||||
| ----------------- | --------------- |
|
||||
| Build command | `npm run build` |
|
||||
| Publish directory | `dist` |
|
||||
| Версія Node | 18+ |
|
||||
|
||||
### Крок 3: Разгортванне
|
||||
|
||||
Націсніце "Deploy site" і пачакайце завяршэння зборкі.
|
||||
|
||||
## Файл канфігурацыі
|
||||
|
||||
Стварыце `netlify.toml` у корані праекта:
|
||||
|
||||
```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 Важна
|
||||
Загалоўкі `Cross-Origin-Embedder-Policy` і `Cross-Origin-Opener-Policy` патрэбныя для пераўтварэнняў Word/ODT/Excel/PowerPoint у PDF. Без іх `SharedArrayBuffer` недаступны, і рухавік LibreOffice WASM не ініцыялізуецца.
|
||||
|
||||
Загалоўкі `Content-Encoding: gzip` для файлаў `.wasm.gz` і `.data.gz` кажуць браўзеру распакоўваць іх аўтаматычна. Без іх браўзер атрымлівае сырыя байты gzip, і кампіляцыя WASM завяршаецца памылкай.
|
||||
:::
|
||||
|
||||
## Зменныя асяроддзя
|
||||
|
||||
Задайце іх у Site settings → Environment variables:
|
||||
|
||||
| Зменная | Апісанне |
|
||||
| ----------------------- | -------------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Задайце `true` для мінімальнай зборкі |
|
||||
| `VITE_BRAND_NAME` | Уласная назва брэнда (замяняе "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Шлях да лагатыпа адносна `public/` (напрыклад, `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Уласны тэкст ніжняга калантытула/аўтарскага права |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Мова інтэрфейсу па змаўчанні (напрыклад, `fr`, `de`, `es`) |
|
||||
|
||||
## Уласны дамен
|
||||
|
||||
1. Перайдзіце ў Site settings → Domain management
|
||||
2. Націсніце "Add custom domain"
|
||||
3. Выконвайце інструкцыі па наладзе DNS
|
||||
|
||||
## Вялікія медыяфайлы
|
||||
|
||||
Для вялікіх файлаў WASM варта ўключыць [Netlify Large Media](https://docs.netlify.com/large-media/overview/):
|
||||
|
||||
```bash
|
||||
netlify lm:setup
|
||||
git lfs track "*.wasm"
|
||||
```
|
||||
|
||||
## Выпраўленне непаладак
|
||||
|
||||
### Word/ODT/Excel у PDF спыняецца на 55%
|
||||
|
||||
Калі пераўтварэнне дакументаў завісае на 55%, адкрыйце кансоль DevTools і праверце:
|
||||
|
||||
```js
|
||||
console.log(window.crossOriginIsolated); // should be true
|
||||
console.log(typeof SharedArrayBuffer); // should be "function"
|
||||
```
|
||||
|
||||
Калі `crossOriginIsolated` роўна `false`, загалоўкі COEP/COOP з вашага `netlify.toml` не прымяняюцца. Пераканайцеся, што файл ляжыць у корані праекта, і разгарніце нанова.
|
||||
|
||||
Калі ў кансолі бачна `expected magic word 00 61 73 6d, found 1f 8b 08 08`, у файлаў `.wasm.gz` няма загалоўкаў `Content-Encoding: gzip`. Пераканайцеся, што блокі `[[headers]]` для `soffice.wasm.gz` і `soffice.data.gz` ёсць у вашым `netlify.toml`.
|
||||
|
||||
### Памылка зборкі
|
||||
|
||||
Праверце сумяшчальнасць версіі Node:
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
```
|
||||
|
||||
### Павольная першая загрузка
|
||||
|
||||
Уключыце аптымізацыю рэсурсаў у Site settings → Build & deploy → Asset optimization.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Разгортванне з Nginx
|
||||
|
||||
Размясціце BentoPDF на ўласным серверы з дапамогай Nginx.
|
||||
|
||||
## Патрабаванні
|
||||
|
||||
- Сервер Ubuntu/Debian
|
||||
- Усталяваны Nginx
|
||||
- SSL-сертыфікат (рэкамендуецца Let's Encrypt)
|
||||
|
||||
## Крок 1: Зборка праекта
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alam00000/bentopdf.git
|
||||
cd bentopdf
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Каб змяніць брэндынг, задайце зменныя асяроддзя перад зборкай:
|
||||
|
||||
```bash
|
||||
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
|
||||
```
|
||||
|
||||
## Крок 2: Капіраванне файлаў
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/bentopdf
|
||||
sudo cp -r dist/* /var/www/bentopdf/
|
||||
sudo chown -R www-data:www-data /var/www/bentopdf
|
||||
```
|
||||
|
||||
## Крок 3: Канфігурацыя Nginx
|
||||
|
||||
Стварыце `/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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Крок 4: Уключэнне сайта
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Крок 5: SSL з Let's Encrypt
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
```
|
||||
|
||||
## Разгортванне ў падкаталогу
|
||||
|
||||
Каб размясціць па шляху `/pdf/`:
|
||||
|
||||
```nginx
|
||||
location /pdf/ {
|
||||
alias /var/www/bentopdf/;
|
||||
try_files $uri $uri/ /pdf/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
Збярыце з:
|
||||
|
||||
```bash
|
||||
BASE_URL=/pdf/ npm run build
|
||||
```
|
||||
|
||||
## Налада прадукцыйнасці
|
||||
|
||||
Дадайце ў `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;
|
||||
}
|
||||
```
|
||||
|
||||
## Выпраўленне непаладак
|
||||
|
||||
### WASM не загружаецца
|
||||
|
||||
Пераканайцеся, што MIME-тып зададзены:
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
### Падпісаць PDF або Запаўненне форм паказвае пусты праглядальнік (памылка MIME `.mjs`)
|
||||
|
||||
Калі ў кансолі браўзера бачна:
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
…і няўдалы запыт заканчваецца на `.mjs`, у вашым nginx няма адпаведнасці `.mjs → application/javascript`. Убудаваны праглядальнік PDF (выкарыстоўваецца ў Падпісаць PDF, Запаўненні форм і некалькіх іншых інструментах) пастаўляецца з файламі ES-модуляў з пашырэннем `.mjs`, а стандартны `mime.types` у nginx па змаўчанні іх не ўключае.
|
||||
|
||||
Выправіце гэта, дадаўшы `mjs` у існуючы радок `application/javascript` у `/etc/nginx/mime.types`:
|
||||
|
||||
```nginx
|
||||
application/javascript js mjs;
|
||||
```
|
||||
|
||||
> НЕ дадавайце _другі_ радок `application/javascript`: nginx разбірае `mime.types` радок за радком, і перамагае толькі адна адпаведнасць тыпу і пашырэння, таму ў некаторых версіях другі радок моўчкі ігнаруецца. Адрэдагуйце існуючы радок, дадаўшы `mjs` побач з `js`.
|
||||
|
||||
Або перавызначце лакальна ўнутры блока server (без рэдагавання глабальнага файла):
|
||||
|
||||
```nginx
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
application/wasm wasm;
|
||||
}
|
||||
```
|
||||
|
||||
Перазагрузіце nginx пасля змены:
|
||||
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Каб пераканацца: у DevTools браўзера → укладка Network знайдзіце няўдалы запыт `.mjs` і праверце загаловак адказу `Content-Type`. Цяпер ён павінен быць `application/javascript`, а не `application/octet-stream`.
|
||||
|
||||
### Word/ODT/Excel у PDF не працуе
|
||||
|
||||
LibreOffice WASM патрабуе `SharedArrayBuffer`, для чаго патрэбныя загалоўкі `Cross-Origin-Embedder-Policy` і `Cross-Origin-Opener-Policy`. Таксама патрэбны бяспечны кантэкст, таму `http://localhost` працуе для лакальнай праверкі, а `http://192.168.x.x` ці іншыя IP лакальнай сеткі звычайна патрабуюць HTTPS. Звярніце ўвагу: дырэктывы nginx `add_header` у блоку `location` **перавызначаюць** дырэктывы `add_header` узроўню server, а не аб'ядноўваюцца з імі. Кожны блок `location` з уласным `add_header` павінен уключаць загалоўкі COEP/COOP.
|
||||
|
||||
Праверце з дапамогай:
|
||||
|
||||
```bash
|
||||
curl -I https://your-domain.com/ | grep -i cross-origin
|
||||
```
|
||||
|
||||
Калі перад nginx стаіць зваротны проксі, пераканайцеся, што ён захоўвае гэтыя загалоўкі.
|
||||
|
||||
### 502 Bad Gateway
|
||||
|
||||
Праверце журналы памылак Nginx:
|
||||
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
# Разгортванне на Vercel
|
||||
|
||||
[Vercel](https://vercel.com) прапануе самы просты спосаб разгортвання статычных сайтаў.
|
||||
|
||||
## Разгортванне адным клікам
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/alam00000/bentopdf)
|
||||
|
||||
## Ручное разгортванне
|
||||
|
||||
### Крок 1: Зрабіце форк рэпазіторыя
|
||||
|
||||
Зрабіце форк [bentopdf/bentopdf](https://github.com/alam00000/bentopdf) у свой уліковы запіс GitHub.
|
||||
|
||||
### Крок 2: Імпарт у Vercel
|
||||
|
||||
1. Перайдзіце на [vercel.com/new](https://vercel.com/new)
|
||||
2. Выберыце свой форк рэпазіторыя
|
||||
3. Наладзьце праект:
|
||||
|
||||
| Налада | Значэнне |
|
||||
| ---------------- | --------------- |
|
||||
| Framework Preset | Vite |
|
||||
| Build Command | `npm run build` |
|
||||
| Output Directory | `dist` |
|
||||
| Install Command | `npm install` |
|
||||
|
||||
### Крок 3: Зменныя асяроддзя (неабавязкова)
|
||||
|
||||
Дадайце пры неабходнасці:
|
||||
|
||||
| Зменная | Апісанне |
|
||||
| ----------------------- | -------------------------------------------------------------------- |
|
||||
| `SIMPLE_MODE` | Задайце `true` для мінімальнага інтэрфейсу |
|
||||
| `VITE_BRAND_NAME` | Уласная назва брэнда (замяняе "BentoPDF") |
|
||||
| `VITE_BRAND_LOGO` | Шлях да лагатыпа адносна `public/` (напрыклад, `images/my-logo.svg`) |
|
||||
| `VITE_FOOTER_TEXT` | Уласны тэкст ніжняга калантытула/аўтарскага права |
|
||||
| `VITE_DEFAULT_LANGUAGE` | Мова інтэрфейсу па змаўчанні (напрыклад, `fr`, `de`, `es`) |
|
||||
|
||||
### Крок 4: Разгортванне
|
||||
|
||||
Націсніце "Deploy" і пачакайце завяршэння зборкі.
|
||||
|
||||
## Уласны дамен
|
||||
|
||||
1. Адкрыйце налады праекта
|
||||
2. Перайдзіце ў "Domains"
|
||||
3. Дадайце свой дамен
|
||||
4. Наладзьце DNS паводле інструкцый
|
||||
|
||||
## Абмежаванні
|
||||
|
||||
::: warning Вялікія файлы
|
||||
Бессерверныя функцыі Vercel маюць абмежаванне 50MB. Паколькі BentoPDF з'яўляецца статычным сайтам, гэта не павінна вас закрануць, але модулі WASM вялікія (каля 100MB агулам). Пераканайцеся, што яны раздаюцца з папкі `/public`.
|
||||
:::
|
||||
|
||||
## Выпраўленне непаладак
|
||||
|
||||
### Перавышэнне часу зборкі
|
||||
|
||||
Калі зборка перавышае час чакання, паспрабуйце:
|
||||
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": "dist"
|
||||
}
|
||||
```
|
||||
|
||||
### 404 пры абнаўленні старонкі
|
||||
|
||||
Дадайце `vercel.json` для маршрутызацыі SPA:
|
||||
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
```
|
||||
|
||||
### Word/ODT/Excel у PDF не працуе
|
||||
|
||||
Пераўтварэнні LibreOffice WASM патрабуюць `SharedArrayBuffer`, для чаго на ўсіх старонках патрэбныя такія загалоўкі адказу:
|
||||
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
|
||||
Папярэдне сціснутым файлам `.wasm.gz` і `.data.gz` таксама патрэбны `Content-Encoding: gzip`, каб браўзер іх распакоўваў.
|
||||
|
||||
Дадайце гэта ў свой `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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Каб праверыць, адкрыйце кансоль DevTools і выканайце `console.log(window.crossOriginIsolated)`: павінна вярнуцца `true`.
|
||||
Reference in new issue
Block a user