NEXT_PUBLIC_VAPID_PUBLIC_KEY (and the OAuth *_ENABLED flags) are inlined
into the client bundle by next build — setting them as container env vars
at runtime does nothing, since the client JS was already built without
them. Client bundle shipped with applicationServerKey: undefined, so
pushManager.subscribe() threw and the button always failed with "Failed
to enable notifications."
Wire them as Docker build args (Dockerfile ARG/ENV before `next build`,
compose.prod.yml build.args), sourced from the same stack env vars used
at runtime so they stay in sync.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
compose.prod.yml only whitelisted DB/redis/storage/auth/VAPID/Google/
OpenRouter vars in web.environment — Portainer stack env alone doesn't
reach the container unless the compose file declares the ${VAR} ref, so
SMTP_HOST etc were set in Portainer but silently ignored. Add the full
set from .env.example (SMTP, GitHub/Discord/Authentik OAuth + their
NEXT_PUBLIC_*_ENABLED flags, Stripe, remaining AI provider keys).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
login page referenced auth.continueWithGithub/Discord/Authentik keys that
didn't exist in en/fr message files, crashing the login page. Also remap
docker/compose.yml redis to 6380 since 6379 collides with an unrelated
local container on this machine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a migrator Dockerfile target (drizzle-kit migrate + tier-definitions
seed, both idempotent) and wire it as a one-shot compose service that web
waits on via service_completed_successfully. No more manual exec step
after first deploy — runs safely on every redeploy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add root Dockerfile (standalone Next output, multi-stage pnpm build), drop
in-stack caddy in favor of publishing web's port for an external traefik
LXC (file-provider dynamic config included), and document the portainer
deploy flow.
Also fixes issues that blocked any production build: a bad auth-client
type cast, the ai SDK's mimeType->mediaType rename, an implicit-any
callback param, and push.ts eagerly calling webpush.setVapidDetails at
module import time (which crashed page-data collection whenever VAPID
env vars weren't present at build) — now lazily configured on first send.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sticky sidebar with back-to-app link. Overview: user counts, recipe photos, AI calls/month.
User management with role/tier editing. Audit log (all admin actions).
Storage page with photo breakdown by tier. AI config showing DB vs .env key source.
Site settings page to override .env values at runtime (encrypted in DB).
Follow/unfollow users. Recipe favorites. Threaded comments with emoji reactions.
Collections (public/private) with shared member invite. Activity feed.
Public profile pages at /u/[username].