feat(deploy): make the all-in-one image installable without a shell (#1124)
The all-in-one image could not be installed from a GUI at all — the deployment it
exists for. validateEnv treats a missing JWT_SECRET as critical and exits, and the
documented run command supplies it with `openssl rand`, a shell command a Synology
Container Manager or QNAP Container Station form cannot run.
wait-for-db.sh now generates one on first start and persists it next to the database,
extending the existing /run/secrets hydration rather than adding a second mechanism.
Explicit env still wins, then /run/secrets, then the generated file. The write is
load-bearing: JWT_SECRET is exported only when the file actually persisted, because an
unpersisted secret would mint a new one every restart and sign every session out.
Creation writes to a private temp file and hard-links it into place — atomic, fails with
EEXIST when another container won, and the loser adopts the winner's value. Non-regular
paths are rejected before the link, since POSIX ln links INTO a directory rather than
failing, which would make a mistyped -v target unrecoverable.
Also repairs the onboarding paths a new install actually walks: the installer no longer
rotates the secrets of a running install on re-run, deprecates the dead scripts/install.sh
in place, corrects the CONTRIBUTING dev loop, and fixes the vite proxy target that had
been pointing at a stray local port since 0da45e69.
Reviewed over three rounds. Co-authored by @Luca-Timo.
This commit is contained in:
@@ -110,9 +110,20 @@ services:
|
||||
- FACE_ML_TOKEN=${FACE_ML_TOKEN:-}
|
||||
- FACE_PROCESSOR_CONCURRENCY=${FACE_PROCESSOR_CONCURRENCY:-}
|
||||
volumes:
|
||||
- ${APP_STORAGE}:/app/storage
|
||||
- ${LOGS}:/app/logs
|
||||
- ${APP_DATA}:/app/data
|
||||
# Defaulted so a .env that simply does not set these still works. Without
|
||||
# them compose aborts with "invalid spec: :/app/storage: empty section
|
||||
# between colons", which reads like a broken compose file rather than a
|
||||
# missing variable (#705).
|
||||
#
|
||||
# Note this does NOT make `config` work with no .env at all: `env_file`
|
||||
# above still requires the file. Making it optional needs
|
||||
# `required: false`, which is Compose 2.24+ syntax that OLDER Compose
|
||||
# rejects as a schema error — taking the whole stack down rather than
|
||||
# just losing a default. Not worth it for a case the onboarding flow
|
||||
# never hits, since it copies .env.example first.
|
||||
- ${APP_STORAGE:-./storage}:/app/storage
|
||||
- ${LOGS:-./logs}:/app/logs
|
||||
- ${APP_DATA:-./data}:/app/data
|
||||
- picpeak-secrets:/run/secrets:ro
|
||||
ports:
|
||||
- "${BACKEND_PORT:-3001}:3000"
|
||||
@@ -182,7 +193,7 @@ services:
|
||||
# Uses same channel as backend for consistency
|
||||
image: ghcr.io/picpeak/picpeak/frontend:${PICPEAK_CHANNEL:-stable}
|
||||
container_name: picpeak-frontend
|
||||
# Note: Pre-built frontend uses Nginx to proxy /api to backend:3001.
|
||||
# Note: Pre-built frontend uses Nginx to proxy /api to backend:3000.
|
||||
# Prefer keeping API base as '/api' in builds to avoid CORS.
|
||||
environment:
|
||||
# Substituted into index.html at container start (see frontend/
|
||||
|
||||
Reference in New Issue
Block a user