added doc

This commit is contained in:
Patrick Plate
2026-06-22 11:38:09 +02:00
parent e18136baa4
commit 02844e4c4a
@@ -0,0 +1,89 @@
# 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.ts``session()` 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).