# AdventureLog Setup Summary ## What It Is AdventureLog is a self-hosted travel logging app with a SvelteKit frontend and Django/PostGIS backend. Live at: **https://go.jgitta.com** --- ## Infrastructure | Component | Detail | |-----------|--------| | Host | siklos (192.168.88.27) | | Portainer Stack | #47 — `adventurelog` (endpoint 3) | | Frontend container | `adventurelog-frontend` — port 8015 (SvelteKit SSR) | | Backend container | `adventurelog-backend` — port 8016 (Django/nginx → gunicorn :8000) | | Database container | `adventurelog-db` (PostGIS 16-3.5) | | Data volumes | `/srv/docker/adventurelog/postgres_data/` and `/srv/docker/adventurelog/media/` | | Reverse proxy | Caddy VM (192.168.88.110) — `go.jgitta.com` block in `/etc/caddy/sites/infrastructure.caddy` | --- ## Caddy Routing (Corrected 2026-07-02 — now matches official docs) AdventureLog authenticates through the SvelteKit frontend: on login the frontend keeps the Django session **server-side** and does NOT hand the browser a Django `sessionid` cookie (it even clears `csrftoken` in the browser). Therefore **all `/api/*` and `/auth/*` traffic must go through the frontend**, which proxies to Django server-to-server with the correct session + CSRF token. Only static-type paths go straight to Django. This is the official AdventureLog Caddy design (https://adventurelog.app/docs/install/caddy.html), adapted for our remote Caddy VM (proxying to LAN ports 8015/8016) plus our standard security headers: ```caddy go.jgitta.com { import headers_base encode zstd gzip header { X-Frame-Options "SAMEORIGIN" Permissions-Policy "geolocation=(self), microphone=(), camera=()" } # Backend (Django): media, admin, static files, legacy account pages @backend path /media* /admin* /static* /accounts* handle @backend { reverse_proxy http://192.168.88.27:8016 { import proxy_timeouts } } # Everything else (pages, /api/*, /auth/*) -> SvelteKit frontend, # which proxies API/auth to Django server-side with session + CSRF. handle { reverse_proxy http://192.168.88.27:8015 { import proxy_timeouts } } } ``` **`geolocation=(self)`** (inline, not the `web_secure` snippet) is required so the "Use My Location" button works. ### History — why the old split routing was removed Earlier the config split `/api/*` by HTTP method: GET/HEAD/OPTIONS went **directly to Django** and only writes went through the frontend. That was a workaround for a geocode 401 seen during initial setup. After a frontend container auto-update (`:latest`) on 2026-07-02, the frontend stopped exposing a Django session to the browser, so the "GET direct to Django" path could no longer authenticate — every authenticated read (including **geocoding / map search**) returned `401 "Authentication credentials were not provided."` Reverting to the official all-`/api`-through- frontend routing fixed it. Lesson: don't hand-split `/api`; let the frontend proxy it. --- ## Portainer Environment Variables All env vars are stored in Portainer's environment section (not hardcoded in compose). Key ones: | Variable | Value | |----------|-------| | PUBLIC_SERVER_URL | http://server:8000 | | ORIGIN | https://go.jgitta.com | | PUBLIC_URL | https://go.jgitta.com | | CSRF_TRUSTED_ORIGINS | https://go.jgitta.com | | FRONTEND_URL | https://go.jgitta.com | | FRONTEND_PORT | 8015 | | BACKEND_PORT | 8016 | | BODY_SIZE_LIMIT | Infinity | | POSTGRES_DB | adventurelog | | POSTGRES_USER | adventure | | DEBUG | False | | DISABLE_REGISTRATION | False | Secrets (DB password, SECRET_KEY, admin credentials) are in Portainer only — not in this file. Note: `DJANGO_ADMIN_PASSWORD` is only the *bootstrap* password used at first container start; the live admin password has since been changed in-app. Optional/recommended (not currently set): `ENABLE_RATE_LIMITS=True` (docs recommend for production). --- ## Bugs Fixed During Setup 1. **CSRF errors on login** — `CSRF_TRUSTED_ORIGINS` must match the public URL exactly. Set in env. 2. **GET /api/* returning DRF HTML instead of JSON** — old Caddy did a broken URI rewrite; removed. 3. **Geocode search 401** — see "Caddy Routing" history above. Final fix: route all `/api/*` through the SvelteKit frontend (official design), not directly to Django. 4. **Category creation 500** — AdventureLog returns 500 (not 400) on duplicate category slug (unhandled IntegrityError). Workaround: delete the duplicate via Django shell. 5. **"Use My Location" blocked** — set `Permissions-Policy: geolocation=(self)` on the site block. --- ## Admin Access - **URL**: https://go.jgitta.com/admin - **Username**: admin - **Django shell**: `docker exec -it adventurelog-backend python manage.py shell` --- ## Known Limitations / Not Configured - Images run on `:latest` with Watchtower auto-update — an update on 2026-07-02 changed frontend auth behavior and broke the old routing. Consider pinning image versions. - No Google Maps API key — geocoding uses OpenStreetMap/Nominatim (works fine). - No Strava integration. - B2/Kopia backup not configured for this stack.