efa6b4a205
@Rekoo-PS confirmed the prior #521 fix landed on beta but reported the preview still shows the default "PicPeak" title — their brand is "arkan-studio". Root cause: that fix used Vite's build-time %VITE_DEFAULT_TITLE% substitution. Self-hosters running the pre-built ghcr.io/the-luap/picpeak/frontend image can't override at build time without rebuilding, so they were stuck with whatever the upstream build baked in. Pivot to runtime substitution: the frontend container now reads BRAND_TITLE / BRAND_DESCRIPTION env vars on startup and envsubsts them into index.html. Change the values in .env, restart the frontend service, done — no rebuild required. Mechanics: - frontend/index.html: tokens are now ${BRAND_TITLE} / ${BRAND_DESCRIPTION} (shell expansion syntax, passes through Vite unchanged into the built dist). - frontend/Dockerfile: install gettext (provides envsubst), snapshot /usr/share/nginx/html/index.html → index.html.tpl at build, install docker-entrypoint.sh, wire ENTRYPOINT to it. The .tpl is the immutable source — every container start re-renders index.html from .tpl, so restarts pick up new env values cleanly (no accidental "first-boot env stuck forever" trap). - frontend/docker-entrypoint.sh: applies defaults if env unset, runs envsubst (locked to BRAND_TITLE + BRAND_DESCRIPTION explicitly so /assets/*.js template literals aren't touched if anyone ever extends substitution to the bundle), execs nginx. - frontend/vite.config.ts: drop the htmlTitleDefaults plugin — no longer needed since substitution is fully runtime. - frontend/.env.example + .env.production.example: drop the VITE_DEFAULT_* docs (the vars no longer have effect). - docker-compose.yml + docker-compose.production.yml: pass BRAND_TITLE / BRAND_DESCRIPTION env into the frontend service with sensible defaults so unconfigured installs work unchanged. - .env.example: add BRAND_TITLE / BRAND_DESCRIPTION with comment pointing at the social-preview use case. Verified end-to-end against the built image: - BRAND_TITLE="Arkan Studio" BRAND_DESCRIPTION="Wedding photographs by Arkan Studio" → index.html serves <title>Arkan Studio</title> + og:title="Arkan Studio" + og:description correctly substituted. - .tpl preserves ${...} tokens so the next restart can re-substitute. - Bundle assets unaffected. - Defaults applied when env unset → <title>PicPeak</title>. Docs PR in picpeak-docs describes the two new env vars under "Social link preview fallback" in the environment-variables reference. Refs: #521
149 lines
4.3 KiB
YAML
149 lines
4.3 KiB
YAML
services:
|
|
backend:
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile
|
|
container_name: picpeak-backend
|
|
restart: unless-stopped
|
|
environment:
|
|
- NODE_ENV=${NODE_ENV:-production}
|
|
- PORT=3000
|
|
- JWT_SECRET=${JWT_SECRET}
|
|
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
|
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
|
|
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
|
- DATABASE_CLIENT=pg
|
|
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
|
- DB_TYPE=postgresql
|
|
- DB_HOST=postgres
|
|
- DB_PORT=5432
|
|
- DB_USER=${DB_USER}
|
|
- DB_PASSWORD=${DB_PASSWORD}
|
|
- DB_NAME=${DB_NAME}
|
|
- EXTERNAL_MEDIA_ROOT=${EXTERNAL_MEDIA_ROOT:-/app/storage/external-media}
|
|
- SMTP_HOST=${SMTP_HOST}
|
|
- SMTP_PORT=${SMTP_PORT}
|
|
- SMTP_SECURE=${SMTP_SECURE:-false}
|
|
- SMTP_USER=${SMTP_USER}
|
|
- SMTP_PASS=${SMTP_PASS}
|
|
- EMAIL_FROM=${EMAIL_FROM:-noreply@picpeak.local}
|
|
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
|
|
- ADMIN_URL=${ADMIN_URL:-http://localhost:3001}
|
|
- TZ=${TZ:-UTC}
|
|
- STORAGE_PATH=/app/storage
|
|
# No `user:` directive — as of #484, the container starts as root,
|
|
# chowns the bind mounts to nodejs (UID 1001), then drops privileges
|
|
# via su-exec. PUID/PGID env vars are no longer read; if you need
|
|
# a different runtime UID, pre-chown the host dirs and pin
|
|
# `user: "<uid>:<gid>"` here.
|
|
volumes:
|
|
- ./events:/app/events
|
|
- ./data:/app/data
|
|
- ./logs:/app/logs
|
|
- ./backup:/backup
|
|
- ./storage:/app/storage
|
|
ports:
|
|
- "${BACKEND_PORT:-3001}:3000"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
networks:
|
|
- picpeak-network
|
|
|
|
postgres:
|
|
image: postgres:15-alpine
|
|
container_name: picpeak-postgres
|
|
restart: unless-stopped
|
|
userns_mode: "host"
|
|
environment:
|
|
- POSTGRES_USER=${DB_USER}
|
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
- POSTGRES_DB=${DB_NAME}
|
|
- PGDATA=/var/lib/postgresql/data/pgdata
|
|
- TZ=${TZ:-UTC}
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
ports:
|
|
- "127.0.0.1:${DB_PORT:-5432}:5432"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 20s
|
|
networks:
|
|
- picpeak-network
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: picpeak-redis
|
|
restart: unless-stopped
|
|
userns_mode: "host"
|
|
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-picpeak_redis_pass}
|
|
volumes:
|
|
- redis-data:/data
|
|
ports:
|
|
- "127.0.0.1:${REDIS_PORT:-6379}:6379"
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
networks:
|
|
- picpeak-network
|
|
|
|
mailhog:
|
|
image: mailhog/mailhog:latest
|
|
container_name: picpeak-mailhog
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${MAILHOG_SMTP_PORT:-1025}:1025"
|
|
- "${MAILHOG_UI_PORT:-8025}:8025"
|
|
networks:
|
|
- picpeak-network
|
|
|
|
frontend:
|
|
build:
|
|
context: ./frontend
|
|
dockerfile: Dockerfile
|
|
args:
|
|
- VITE_API_URL=${VITE_API_URL:-/api}
|
|
- VITE_UMAMI_URL=${VITE_UMAMI_URL:-}
|
|
- VITE_UMAMI_WEBSITE_ID=${VITE_UMAMI_WEBSITE_ID:-}
|
|
- VITE_UMAMI_SHARE_URL=${VITE_UMAMI_SHARE_URL:-}
|
|
container_name: picpeak-frontend
|
|
restart: unless-stopped
|
|
environment:
|
|
- NODE_ENV=${NODE_ENV:-production}
|
|
# Static social-preview brand (#521) — substituted into
|
|
# index.html at container start; see frontend/docker-entrypoint.sh.
|
|
- BRAND_TITLE=${BRAND_TITLE:-PicPeak}
|
|
- BRAND_DESCRIPTION=${BRAND_DESCRIPTION:-Photo gallery shared with PicPeak.}
|
|
ports:
|
|
- "${FRONTEND_PORT:-3000}:80"
|
|
depends_on:
|
|
- backend
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks:
|
|
- picpeak-network
|
|
|
|
volumes:
|
|
postgres-data:
|
|
driver: local
|
|
redis-data:
|
|
driver: local
|
|
|
|
networks:
|
|
picpeak-network:
|
|
driver: bridge
|