AdventureLog: correct to final split config + slash-rewrite; document Google Maps enablement
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## What It Is
|
## What It Is
|
||||||
AdventureLog is a self-hosted travel logging app with a SvelteKit frontend and Django/PostGIS backend.
|
AdventureLog is a self-hosted travel logging app with a SvelteKit frontend and Django/PostGIS backend.
|
||||||
Live at: **https://go.jgitta.com**
|
Live at: **https://go.jgitta.com** — version **v0.12.1**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -16,20 +16,21 @@ Live at: **https://go.jgitta.com**
|
|||||||
| Backend container | `adventurelog-backend` — port 8016 (Django/nginx → gunicorn :8000) |
|
| Backend container | `adventurelog-backend` — port 8016 (Django/nginx → gunicorn :8000) |
|
||||||
| Database container | `adventurelog-db` (PostGIS 16-3.5) |
|
| Database container | `adventurelog-db` (PostGIS 16-3.5) |
|
||||||
| Data volumes | `/srv/docker/adventurelog/postgres_data/` and `/srv/docker/adventurelog/media/` |
|
| 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` |
|
| Reverse proxy | Caddy VM (192.168.88.110) — `go.jgitta.com` in `/etc/caddy/sites/infrastructure.caddy` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Caddy Routing (Corrected 2026-07-02 — now matches official docs)
|
## Caddy Routing (final, verified 2026-07-02)
|
||||||
|
|
||||||
AdventureLog authenticates through the SvelteKit frontend: on login the frontend keeps the
|
Use the **split** config below. The browser holds a valid Django session, so routing reads
|
||||||
Django session **server-side** and does NOT hand the browser a Django `sessionid` cookie (it even
|
directly to Django works. Routing reads through the SvelteKit frontend instead reintroduces a
|
||||||
clears `csrftoken` in the browser). Therefore **all `/api/*` and `/auth/*` traffic must go through
|
trailing-slash redirect that breaks the category dropdown — do **not** switch to the official
|
||||||
the frontend**, which proxies to Django server-to-server with the correct session + CSRF token.
|
docs' single-proxy config.
|
||||||
Only static-type paths go straight to Django.
|
|
||||||
|
|
||||||
This is the official AdventureLog Caddy design (https://adventurelog.app/docs/install/caddy.html),
|
The key fix: the app requests `/api/...` **without a trailing slash**; Django (DRF) then answers
|
||||||
adapted for our remote Caddy VM (proxying to LAN ports 8015/8016) plus our standard security headers:
|
`301` to add it, and that redirect breaks the browser fetch (empty search / empty categories). The
|
||||||
|
`@api_read` block rewrites the path to add the slash **internally**, so Django answers `200`
|
||||||
|
directly with no redirect.
|
||||||
|
|
||||||
```caddy
|
```caddy
|
||||||
go.jgitta.com {
|
go.jgitta.com {
|
||||||
@@ -40,83 +41,74 @@ go.jgitta.com {
|
|||||||
Permissions-Policy "geolocation=(self), microphone=(), camera=()"
|
Permissions-Policy "geolocation=(self), microphone=(), camera=()"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Backend (Django): media, admin, static files, legacy account pages
|
# GET/HEAD/OPTIONS /api/* -> Django directly (browser sends session cookie)
|
||||||
@backend path /media* /admin* /static* /accounts*
|
@api_read { method GET HEAD OPTIONS; path /api/* }
|
||||||
handle @backend {
|
handle @api_read {
|
||||||
reverse_proxy http://192.168.88.27:8016 { import proxy_timeouts }
|
# DRF needs a trailing slash; add it internally to avoid a 301 that breaks the fetch
|
||||||
|
@api_noslash path_regexp apinoslash ^(/api/(?:.*/)?[^/.]+)$
|
||||||
|
rewrite @api_noslash {re.apinoslash.1}/
|
||||||
|
reverse_proxy http://192.168.88.27:8016 {
|
||||||
|
import proxy_timeouts
|
||||||
|
header_up Accept "application/json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Everything else (pages, /api/*, /auth/*) -> SvelteKit frontend,
|
# POST/PUT/DELETE /api/* + pages -> SvelteKit frontend
|
||||||
# which proxies API/auth to Django server-side with session + CSRF.
|
@frontend { not path /media* /admin* /static* /accounts* }
|
||||||
handle {
|
handle @frontend { reverse_proxy http://192.168.88.27:8015 { import proxy_timeouts } }
|
||||||
reverse_proxy http://192.168.88.27:8015 { import proxy_timeouts }
|
|
||||||
}
|
# Django: media, admin, static, auth pages
|
||||||
|
handle { reverse_proxy http://192.168.88.27:8016 { import proxy_timeouts } }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**`geolocation=(self)`** (inline, not the `web_secure` snippet) is required so the "Use My Location"
|
`geolocation=(self)` (inline) is required for the "Use My Location" button.
|
||||||
button works.
|
Backup of prior config: `/etc/caddy/sites/infrastructure.caddy.bak-20260702-041914`.
|
||||||
|
|
||||||
### 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
|
## Geocoding — Google Maps (enabled 2026-07-02)
|
||||||
|
|
||||||
All env vars are stored in Portainer's environment section (not hardcoded in compose). Key ones:
|
Switched from public OpenStreetMap/Nominatim to Google Maps. Nominatim was flaky: the app fires one
|
||||||
|
search per keystroke, and public Nominatim returned inconsistent/empty results for partial queries
|
||||||
|
and rate-limited the rapid autocomplete.
|
||||||
|
|
||||||
|
- `GOOGLE_MAPS_API_KEY` is set in Portainer stack #47 (env + `server` service in compose).
|
||||||
|
- Google Cloud account: **jgitta@gmail.com**. Required APIs enabled: **Geocoding API** and
|
||||||
|
**Places API (New)**.
|
||||||
|
- Provider is automatic: key present → Google (`search_google`, `reverse_geocode_google`);
|
||||||
|
otherwise OSM. Google also enriches locations (photos, rating, website, phone, description).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Portainer Environment Variables (stack #47)
|
||||||
|
|
||||||
| Variable | Value |
|
| Variable | Value |
|
||||||
|----------|-------|
|
|----------|-------|
|
||||||
| PUBLIC_SERVER_URL | http://server:8000 |
|
| PUBLIC_SERVER_URL | http://server:8000 |
|
||||||
| ORIGIN | https://go.jgitta.com |
|
| ORIGIN / PUBLIC_URL / CSRF_TRUSTED_ORIGINS / FRONTEND_URL | https://go.jgitta.com |
|
||||||
| PUBLIC_URL | https://go.jgitta.com |
|
| FRONTEND_PORT / BACKEND_PORT | 8015 / 8016 |
|
||||||
| CSRF_TRUSTED_ORIGINS | https://go.jgitta.com |
|
|
||||||
| FRONTEND_URL | https://go.jgitta.com |
|
|
||||||
| FRONTEND_PORT | 8015 |
|
|
||||||
| BACKEND_PORT | 8016 |
|
|
||||||
| BODY_SIZE_LIMIT | Infinity |
|
| BODY_SIZE_LIMIT | Infinity |
|
||||||
| POSTGRES_DB | adventurelog |
|
| POSTGRES_DB / POSTGRES_USER | adventurelog / adventure |
|
||||||
| POSTGRES_USER | adventure |
|
| DEBUG / DISABLE_REGISTRATION | False / False |
|
||||||
| DEBUG | False |
|
| GOOGLE_MAPS_API_KEY | (set — Google Maps enabled) |
|
||||||
| DISABLE_REGISTRATION | False |
|
|
||||||
|
|
||||||
Secrets (DB password, SECRET_KEY, admin credentials) are in Portainer only — not in this file.
|
Secrets (DB password, SECRET_KEY, admin creds, Google key) live in Portainer. `DJANGO_ADMIN_PASSWORD`
|
||||||
Note: `DJANGO_ADMIN_PASSWORD` is only the *bootstrap* password used at first container start; the
|
is only the bootstrap password; the live admin password was changed in-app.
|
||||||
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
|
## Known Bug (reported upstream)
|
||||||
|
|
||||||
1. **CSRF errors on login** — `CSRF_TRUSTED_ORIGINS` must match the public URL exactly. Set in env.
|
Selecting a location **search result** overwrites the location **Name** with the reverse-geocoded
|
||||||
2. **GET /api/* returning DRF HTML instead of JSON** — old Caddy did a broken URI rewrite; removed.
|
nearest-object name instead of keeping the search result's name. With OSM this produced "39" (a
|
||||||
3. **Geocode search 401** — see "Caddy Routing" history above. Final fix: route all `/api/*`
|
campsite tagged near Elk Rock State Park's center). The app reverse-geocodes the selected result's
|
||||||
through the SvelteKit frontend (official design), not directly to Django.
|
coordinates and lets that name win. Workaround: retype the Name after selecting. Report drafted in
|
||||||
4. **Category creation 500** — AdventureLog returns 500 (not 400) on duplicate category slug
|
`AdventureLog-BugReport-location-name-overwrite.md`.
|
||||||
(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
|
## Admin / Notes
|
||||||
- **URL**: https://go.jgitta.com/admin
|
- Admin: https://go.jgitta.com/admin (user `admin`). Django shell: `docker exec -it adventurelog-backend python manage.py shell`.
|
||||||
- **Username**: admin
|
- Images run `:latest` with Watchtower auto-update — an update on 2026-07-02 changed frontend behavior mid-debug. Consider pinning versions.
|
||||||
- **Django shell**: `docker exec -it adventurelog-backend python manage.py shell`
|
- Not configured: B2/Kopia backup for this stack, Strava.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user