Two latent production pitfalls, both proven by probing Compose's env_file resolution: - A bcrypt ADMIN_PASSWORD_HASH is full of $; Compose env_file interpolation AND dotenvy variable substitution both eat the $… segments (as unset vars), corrupting the hash so every admin login 401s. Single-quote it so both read it literally — verified correct for env_file (container) and dotenvy 0.15.7 strong-quote (native). Updated .env.example with the quotes + a warning comment. - MEDIA_PATH: pin it to the /media mount in the app 'environment:' block so a stray host path in .env can't leak in and make every upload 500 with EACCES. environment overrides env_file, so the container is always correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
3.9 KiB
Plaintext
59 lines
3.9 KiB
Plaintext
# ── Domain ────────────────────────────────────────────────────────────────────
|
||
# Public domain Caddy will serve and obtain a TLS certificate for.
|
||
DOMAIN=my-event.example.com
|
||
|
||
# ── App server ────────────────────────────────────────────────────────────────
|
||
APP_PORT=3000
|
||
# Set to `production` in real deployments. This activates the secret guard that
|
||
# refuses to boot with placeholder JWT_SECRET / ADMIN_PASSWORD_HASH values.
|
||
# (docker-compose.yml already sets APP_ENV=production for the app service.)
|
||
APP_ENV=production
|
||
|
||
# ── Database ──────────────────────────────────────────────────────────────────
|
||
# Set a strong password and keep it in sync between DATABASE_URL and
|
||
# POSTGRES_PASSWORD. Generate one with: openssl rand -hex 24
|
||
DATABASE_URL=postgres://eventsnap:CHANGE_ME_use_a_strong_password@db:5432/eventsnap
|
||
POSTGRES_USER=eventsnap
|
||
POSTGRES_PASSWORD=CHANGE_ME_use_a_strong_password
|
||
POSTGRES_DB=eventsnap
|
||
|
||
# ── Authentication ────────────────────────────────────────────────────────────
|
||
# Generate with: openssl rand -hex 64
|
||
JWT_SECRET=change_me_to_a_random_64_byte_hex_string
|
||
SESSION_EXPIRY_DAYS=30
|
||
|
||
# Admin dashboard password (bcrypt hash).
|
||
# Generate with: htpasswd -bnBC 12 "" yourpassword | tr -d ':\n'
|
||
# IMPORTANT: keep the SINGLE QUOTES. A bcrypt hash is full of `$` (e.g. $2b$12$…$…),
|
||
# and both Docker Compose's env_file interpolation and dotenvy's variable substitution
|
||
# would otherwise eat the `$…` segments (reading them as unset vars) and corrupt the
|
||
# hash — every admin login then 401s. Single quotes make both read it literally.
|
||
ADMIN_PASSWORD_HASH='$2y$12$placeholder_replace_me'
|
||
|
||
# ── Event ─────────────────────────────────────────────────────────────────────
|
||
EVENT_NAME=Max & Maria's Wedding
|
||
EVENT_SLUG=max-maria-2026
|
||
|
||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||
MEDIA_PATH=/media
|
||
# Export archives (Gallery.zip / Memories.zip). MUST be outside MEDIA_PATH —
|
||
# /media is publicly served, so exports here would be downloadable without auth.
|
||
EXPORT_PATH=/exports
|
||
|
||
# ── Upload limits ─────────────────────────────────────────────────────────────
|
||
DEFAULT_MAX_IMAGE_SIZE_MB=20
|
||
DEFAULT_MAX_VIDEO_SIZE_MB=500
|
||
|
||
# ── Rate limiting ─────────────────────────────────────────────────────────────
|
||
DEFAULT_UPLOAD_RATE_PER_HOUR=10
|
||
DEFAULT_FEED_RATE_PER_MIN=60
|
||
DEFAULT_EXPORT_RATE_PER_DAY=3
|
||
|
||
# ── Capacity ──────────────────────────────────────────────────────────────────
|
||
DEFAULT_ESTIMATED_GUEST_COUNT=100
|
||
# Fraction of total storage that triggers the "low storage" warning (0.0–1.0)
|
||
DEFAULT_QUOTA_TOLERANCE=0.75
|
||
|
||
# ── Workers ───────────────────────────────────────────────────────────────────
|
||
COMPRESSION_WORKER_CONCURRENCY=2
|