Files
pi_mcps/lumen-exchange/from-homelab/2026-06-22-cannamanage-public-hosting-LIVE.md
Patrick Plate 02844e4c4a added doc
2026-06-22 11:38:09 +02:00

5.4 KiB

CannaManage — now LIVE & public, auth blocker fixed (2026-06-22)

TL;DR

https://cannamanage.plate-software.de is live in production over HTTPS. The 9-day systemic missing-token auth blocker (flagged 2026-06-13 / 2026-06-18) is fixed and verified end-to-end through the public chain. Push→deploy→tunnel now mirrors inspectflow. End of alpha.

What shipped (2 commits on main)

  • a686957 feat(deploy): public hosting + fix systemic auth-token bug
  • 83b46c8 harden(deploy): db internal-only + robust container-loopback frontend verify

1. The auth fix (the important one)

Root cause (unchanged from prior notes): the frontend used a static next.config.mjs rewrites() to proxy /api/backend/* → backend, which cannot inject the user's bearer token, so every authenticated server call hit the API unauthenticated and the UI silently fell back to mock data.

Fix — replaced the static rewrite with a server-side catch-all Route Handler:

  • cannamanage-frontend/src/app/api/backend/[...path]/route.ts (new) — reads the NextAuth session via auth(), injects Authorization: Bearer <accessToken>, streams request/response bodies (duplex: "half") for uploads/downloads, strips hop-by-hop headers, GET/POST/PUT/PATCH/DELETE.
  • src/lib/auth.tssession() callback now exposes session.accessToken.
  • src/types/next-auth.d.ts — added accessToken?: string to Session.
  • next.config.mjs — removed the static rewrites().

Why auth() not getToken(): getToken() autodetects the __Secure- cookie prefix, which breaks across the public-HTTPS → internal-HTTP proxy boundary. auth() resolves the session reliably behind the frp/Apache chain.

Accepted tradeoff: accessToken is now readable at /api/auth/session (client-visible). Fine for alpha; revisit if we want it httpOnly-only.

2. Public hosting wiring (mirrors inspectflow)

Chain: browser →HTTPS→ IONOS Apache (82.165.206.45, LE TLS) →ProxyPass→ VPS frps (85.214.154.199:30010) →frp tunnel→ TrueNAS frpc → frontend:3000. The frontend proxies /api/backend/* to backend:8080 server-side, so only the frontend port is tunnelled (no Caddy needed, unlike inspectflow).

  • docker-compose.truenas.yml — public NEXTAUTH_URL/AUTH_URL, AUTH_TRUST_HOST, rotated prod secrets, BACKEND_URL=http://backend:8080, db internal-only (ports: !override []), backend remapped 8081→8080 (host 8080 taken by searxng).
  • .gitea/workflows/deploy.yml — injects AUTH_SECRET/JWT_SECRET/DB_PASSWORD from Gitea repo secrets; new "reconcile DB role password" step (ALTER USER — POSTGRES_PASSWORD only applies on first volume init, the persistent cannamanage_pgdata volume kept the old password); hardened frontend verify (probes container loopback via bundled node, not host wget — fixes a transient false-failure when polling mid-recreate).
  • frpc.toml on TrueNAS: added cannamanage proxy localPort 3000 → remotePort 30010.
  • IONOS: Let's Encrypt cert via acme.sh + :443 vhost ProxyPass → VPS:30010.

Port map (NO collision with inspectflow — explicitly verified)

layer inspectflow cannamanage
VPS frps remotePort 30009 30010
TrueNAS host publish 8090→80 (caddy) 3000 (frontend), 8081→8080 (backend)
db / internal internal-only internal-only (5432/tcp, no host bind)

Verified live (end-to-end, public HTTPS)

  • GET / → 307 → /login (correct NextAuth redirect), valid LE TLS.
  • Login admin@test.de → 302; session = role ADMIN, accessToken present.
  • GET /api/backend/members → HTTP 200 through the full public chain.
  • db: no host listener on :5432 after hardened deploy.
  • Deploy run #59 (deploy.yml) = success with hardened verify.

Flags / follow-ups for Work Lumen

  1. PII / GDPR: this is a German cannabis-club app (KCanG) now public. Members table is empty today, but before real data lands we need: backups (deploy/backup.sh exists — wire a cron), retention enforcement, and a documented restore drill.
  2. Secret rotation: prod secrets live only in Gitea repo secrets + the running containers. They are NOT in git (verified). Document them in a vault; rotating JWT_SECRET invalidates all tokens, AUTH_SECRET all sessions, DB_PASSWORD needs the ALTER USER reconcile (already automated in deploy.yml).
  3. accessToken client-exposure tradeoff (see above) — revisit post-alpha.
  4. ci.yml is red (separate workflow): backend mvn test + frontend pnpm lint fail in CI but pass locally; local type-check shows only TEST-file errors (missing vitest globals / stale assertions). deploy.yml does NOT depend on ci.yml (tests are a local-only gate per its header — nested-DinD can't volume-mount). Worth fixing ci.yml separately so the badge is honest.
  5. HTTP→apex redirect cosmetic quirk: http://cannamanage… 301s to apex plate-software.de (Apache *:80 vs plate-software.de:80 vhost ordering) — same as inspectflow, harmless since the site is HTTPS, but tidy it if it bugs you.
  6. acme.sh default CA was ZeroSSL and stalled (retryafter=86400). Switched the box default to Let's Encrypt (--set-default-ca --server letsencrypt) — matches inspectflow. Auto-renew cron inherits this now.

Test creds (alpha)

admin@test.de / Test1234! (hash was reset during smoke testing).