feat: add support for default UI language configuration and non-root Dockerfile

This commit is contained in:
alam00000
2026-02-13 13:44:56 +05:30
parent d1d354359c
commit 75b1d67fbd
7 changed files with 214 additions and 4 deletions

View File

@@ -11,3 +11,7 @@ VITE_CORS_PROXY_SECRET=
VITE_WASM_PYMUPDF_URL=https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.14/
VITE_WASM_GS_URL=https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm/assets/
VITE_WASM_CPDF_URL=https://cdn.jsdelivr.net/npm/coherentpdf/dist/
# Default UI language (build-time)
# Supported: en, ar, be, fr, de, es, zh, zh-TW, vi, tr, id, it, pt, nl, da
VITE_DEFAULT_LANGUAGE=

View File

@@ -35,6 +35,10 @@ ENV VITE_WASM_PYMUPDF_URL=$VITE_WASM_PYMUPDF_URL
ENV VITE_WASM_GS_URL=$VITE_WASM_GS_URL
ENV VITE_WASM_CPDF_URL=$VITE_WASM_CPDF_URL
# Default UI language (e.g. en, fr, de, es, zh, ar)
ARG VITE_DEFAULT_LANGUAGE
ENV VITE_DEFAULT_LANGUAGE=$VITE_DEFAULT_LANGUAGE
ENV NODE_OPTIONS="--max-old-space-size=3072"
RUN npm run build:with-docs

70
Dockerfile.nonroot Normal file
View File

@@ -0,0 +1,70 @@
# Non-root Dockerfile — supports PUID/PGID environment variables (LSIO-style)
# Usage: docker build -f Dockerfile.nonroot -t bentopdf .
# docker run -d -p 3000:8080 -e PUID=1000 -e PGID=1000 bentopdf
ARG BASE_URL=
# Build stage (identical to main Dockerfile)
FROM public.ecr.aws/docker/library/node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY vendor ./vendor
ENV HUSKY=0
RUN npm config set fetch-retries 5 && \
npm config set fetch-retry-mintimeout 60000 && \
npm config set fetch-retry-maxtimeout 300000 && \
npm config set fetch-timeout 600000 && \
npm ci
COPY . .
ARG SIMPLE_MODE=false
ENV SIMPLE_MODE=$SIMPLE_MODE
ARG COMPRESSION_MODE=all
ENV COMPRESSION_MODE=$COMPRESSION_MODE
ARG BASE_URL
ENV BASE_URL=$BASE_URL
ARG VITE_WASM_PYMUPDF_URL
ARG VITE_WASM_GS_URL
ARG VITE_WASM_CPDF_URL
ENV VITE_WASM_PYMUPDF_URL=$VITE_WASM_PYMUPDF_URL
ENV VITE_WASM_GS_URL=$VITE_WASM_GS_URL
ENV VITE_WASM_CPDF_URL=$VITE_WASM_CPDF_URL
# Default UI language (e.g. en, fr, de, es, zh, ar)
ARG VITE_DEFAULT_LANGUAGE
ENV VITE_DEFAULT_LANGUAGE=$VITE_DEFAULT_LANGUAGE
ENV NODE_OPTIONS="--max-old-space-size=3072"
RUN npm run build:with-docs
# Production stage — uses standard nginx (starts as root, drops to PUID/PGID)
FROM nginx:stable-alpine-slim
LABEL org.opencontainers.image.source="https://github.com/alam00000/bentopdf"
LABEL org.opencontainers.image.url="https://github.com/alam00000/bentopdf"
ARG BASE_URL
ENV PUID=1000
ENV PGID=1000
ENV DISABLE_IPV6=false
RUN apk add --no-cache su-exec
COPY --from=builder /app/dist /usr/share/nginx/html${BASE_URL%/}
COPY nginx.conf /etc/nginx/nginx.conf
COPY --chmod=755 entrypoint.sh /entrypoint.sh
RUN mkdir -p /etc/nginx/tmp \
/var/cache/nginx/client_temp \
/var/cache/nginx/proxy_temp \
/var/cache/nginx/fastcgi_temp \
/var/cache/nginx/uwsgi_temp \
/var/cache/nginx/scgi_temp
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -584,6 +584,14 @@ docker run -p 3000:8080 bentopdf
# The app will be accessible at http://localhost:3000/bentopdf/
```
**Default Language:**
Set the default UI language at build time. Users can still switch languages — this only changes the initial default. Supported: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`.
```bash
docker build --build-arg VITE_DEFAULT_LANGUAGE=fr -t bentopdf .
```
**Combined with Simple Mode:**
```bash
@@ -691,6 +699,26 @@ docker build -t bentopdf .
docker run -p 8080:8080 bentopdf
```
#### Custom User ID (PUID/PGID)
For environments that require running as a specific non-root user (e.g., NAS devices, Kubernetes with security contexts), use the non-root Dockerfile:
```bash
# Build the non-root image
docker build -f Dockerfile.nonroot -t bentopdf-nonroot .
# Run with custom UID/GID
docker run -d -p 3000:8080 -e PUID=1000 -e PGID=1000 bentopdf-nonroot
```
| Variable | Description | Default |
| -------- | ------------------ | ------- |
| `PUID` | User ID to run as | `1000` |
| `PGID` | Group ID to run as | `1000` |
> [!NOTE]
> The standard `Dockerfile` uses `nginx-unprivileged` (UID 101) and is recommended for most deployments. Use `Dockerfile.nonroot` only when you need a specific UID/GID.
For detailed security configuration, see [SECURITY.md](SECURITY.md).
### Digital Signature CORS Proxy (Required)

View File

@@ -95,16 +95,18 @@ docker run -d -p 3000:8080 bentopdf:custom
| `VITE_WASM_PYMUPDF_URL` | PyMuPDF WASM module URL | `https://cdn.jsdelivr.net/npm/@bentopdf/pymupdf-wasm@0.11.14/` |
| `VITE_WASM_GS_URL` | Ghostscript WASM module URL | `https://cdn.jsdelivr.net/npm/@bentopdf/gs-wasm/assets/` |
| `VITE_WASM_CPDF_URL` | CoherentPDF WASM module URL | `https://cdn.jsdelivr.net/npm/coherentpdf/dist/` |
| `VITE_DEFAULT_LANGUAGE` | Default UI language | `en` |
WASM module URLs are pre-configured with CDN defaults — all advanced features work out of the box. Override these for air-gapped or self-hosted deployments.
`VITE_DEFAULT_LANGUAGE` sets the UI language for first-time visitors. Supported values: `en`, `ar`, `be`, `fr`, `de`, `es`, `zh`, `zh-TW`, `vi`, `tr`, `id`, `it`, `pt`, `nl`, `da`. Users can still switch languages — this only changes the default.
Example:
```bash
docker run -d \
-e SIMPLE_MODE=true \
-p 3000:8080 \
ghcr.io/alam00000/bentopdf:latest
# Build with French as the default language
docker build --build-arg VITE_DEFAULT_LANGUAGE=fr -t bentopdf .
docker run -d -p 3000:8080 bentopdf
```
### Custom WASM URLs (Air-Gapped / Self-Hosted)
@@ -147,6 +149,61 @@ docker run -d -p 3000:8080 --restart unless-stopped bentopdf
Set a variable to empty string to disable that module (users must configure manually via Advanced Settings).
## Custom User ID (PUID/PGID)
For environments that require running as a specific non-root user (NAS devices, Kubernetes with security contexts, organizational policies), BentoPDF provides a separate Dockerfile with LSIO-style PUID/PGID support.
### Build and Run
```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
```
### Environment Variables
| Variable | Description | Default |
| -------------- | --------------------- | ------- |
| `PUID` | User ID to run as | `1000` |
| `PGID` | Group ID to run as | `1000` |
| `DISABLE_IPV6` | Disable IPv6 listener | `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
```
### How It Works
The container starts as root, creates a user with the specified PUID/PGID, adjusts ownership on all writable directories, then drops privileges using `su-exec`. The nginx process runs entirely as your specified user.
> [!NOTE]
> The standard `Dockerfile` uses `nginx-unprivileged` (UID 101) and is recommended for most deployments. Use `Dockerfile.nonroot` only when you need a specific UID/GID.
> [!WARNING]
> PUID/PGID cannot be `0` (root). The entrypoint validates inputs and will exit with an error for invalid values.
## With Traefik (Reverse Proxy)
```yaml

42
entrypoint.sh Normal file
View File

@@ -0,0 +1,42 @@
#!/bin/sh
set -e
PUID=${PUID:-1000}
PGID=${PGID:-1000}
# Validate PUID/PGID
case "$PUID" in
''|*[!0-9]*) echo "ERROR: PUID must be a number, got '$PUID'" >&2; exit 1 ;;
esac
case "$PGID" in
''|*[!0-9]*) echo "ERROR: PGID must be a number, got '$PGID'" >&2; exit 1 ;;
esac
if [ "$PUID" -eq 0 ] || [ "$PGID" -eq 0 ]; then
echo "ERROR: PUID/PGID cannot be 0 (root). Use the standard Dockerfile instead." >&2
exit 1
fi
echo "Starting BentoPDF with PUID=$PUID PGID=$PGID"
addgroup -g "$PGID" bentopdf 2>/dev/null || true
adduser -u "$PUID" -G bentopdf -D -H -s /sbin/nologin bentopdf 2>/dev/null || true
rm -f /var/log/nginx/error.log /var/log/nginx/access.log
touch /var/log/nginx/error.log /var/log/nginx/access.log
chown "$PUID:$PGID" /var/log/nginx /var/log/nginx/error.log /var/log/nginx/access.log
sed -i '1i error_log stderr warn;' /etc/nginx/nginx.conf
sed -i '/^http {/a\ access_log /var/log/nginx/access.log;' /etc/nginx/nginx.conf
chown -R "$PUID:$PGID" \
/etc/nginx/tmp \
/var/cache/nginx \
/usr/share/nginx/html \
/etc/nginx/nginx.conf
if [ "$DISABLE_IPV6" = "true" ]; then
echo "Disabling Nginx IPv6 listener"
sed -i '/^[[:space:]]*listen[[:space:]]*\[::\]:[0-9]*/s/^/#/' /etc/nginx/nginx.conf
fi
exec su-exec "$PUID:$PGID" "$@"

View File

@@ -69,6 +69,11 @@ export const getLanguageFromUrl = (): SupportedLanguage => {
return storedLang as SupportedLanguage;
}
const envLang = import.meta.env.VITE_DEFAULT_LANGUAGE;
if (envLang && supportedLanguages.includes(envLang as SupportedLanguage)) {
return envLang as SupportedLanguage;
}
return 'en';
};