8.2 KiB
استقرار با Apache
BentoPDF را با Apache HTTP Server میزبانی کنید.
پیشنیازها
- Apache 2.4+
- فعال بودن mod_rewrite
- گواهی SSL (توصیهشده)
گام ۱: ساخت پروژه
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf
npm install
npm run build
برای سفارشیسازی برند، پیش از ساخت متغیرهای محیطی را تنظیم کنید:
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build
گام ۲: کپی فایلها
sudo mkdir -p /var/www/bentopdf
sudo cp -r dist/* /var/www/bentopdf/
sudo chown -R www-data:www-data /var/www/bentopdf
گام ۳: پیکربندی Apache
فایل /etc/apache2/sites-available/bentopdf.conf را بسازید:
<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>
گام ۴: فایل .htaccess برای مسیریابی
فایل /var/www/bentopdf/.htaccess را بسازید:
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
گام ۵: فعالسازی ماژولهای لازم
sudo a2enmod rewrite
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod expires
sudo a2enmod deflate
گام ۶: فعالسازی سایت
sudo a2ensite bentopdf.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
استقرار در زیرپوشه
برای میزبانی در /pdf/:
- با URL پایه بسازید:
BASE_URL=/pdf/ npm run build
- فایل
.htaccessرا بهروز کنید:
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 پیکربندی شده است:
AddType 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 ختم میشود، پیکربندی Apache شما نگاشت .mjs → application/javascript را ندارد. نمایشگر PDF همراه، فایلهای ماژول ES با پسوند .mjs دارد و mime.types پیشفرض Apache بهطور پیشفرض آنها را شامل نمیشود.
به VirtualHost یا .htaccess خود اضافه کنید:
AddType application/javascript .mjs
سپس Apache را دوباره بارگذاری کنید:
sudo apache2ctl configtest && sudo systemctl reload apache2
برای تأیید: در DevTools مرورگر ← زبانهٔ Network، درخواست ناموفق .mjs را پیدا کنید و سرآیند پاسخ Content-Type را بررسی کنید. اکنون باید application/javascript باشد، نه application/octet-stream.
بازنویسی کار نمیکند
بررسی کنید که mod_rewrite فعال باشد:
sudo a2enmod rewrite
تبدیل Word/ODT/Excel به PDF کار نمیکند
LibreOffice WASM به SharedArrayBuffer نیاز دارد که این سرآیندها را لازم دارد:
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy "same-origin"
همچنین به یک بستر امن (secure context) نیاز دارد. http://localhost برای آزمایش محلی کار میکند، اما http://192.168.x.x یا دیگر IPهای شبکهٔ محلی معمولاً به HTTPS نیاز دارند. اگر سرآیندها وجود دارند اما window.crossOriginIsolated همچنان false است، بررسی کنید که آیا صفحه با HTTP ساده روی مبدأیی غیر از loopback باز میشود.
فایلهای از پیش فشردهشدهٔ .wasm.gz و .data.gz نیز به Content-Encoding درست نیاز دارند:
<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
دسترسی رد شد (Permission Denied)
sudo chown -R www-data:www-data /var/www/bentopdf
sudo chmod -R 755 /var/www/bentopdf