# Session 0025: Dashboard Bugs and SL Routing Fixes **Date:** 2026-02-24 **Status:** Completed **Origin:** MDF Webseiten session 0048 (Part 2 only — DNS cutover and mail recovery sections skipped) --- ## Work Done ### Operations Page: Recreate Replaced by Backup + Restore - [x] Removed "Recreate" lifecycle action (redundant with Rebuild for bind-mount projects) - [x] Added **Backup** button (blue): opens lifecycle modal with SSE streaming to `/api/backups/stream/{project}/{env}` - [x] Added **Restore** button (purple): navigates to Backups page at drill level 2 for that project/env - [x] Added cache invalidation on backup success ### SeriousLetter Bad Gateway Fix - [x] Diagnosed root cause: SL containers only on `seriousletter-network`, not on `proxy` network Traefik uses - [x] Permanent fix: added `proxy` network to docker-compose.yaml for all 3 SL envs (prod/int/dev) - `backend` and `frontend` services get `proxy` in networks list - `proxy: external: true` added to networks section - [x] Added health checks for both services: - Backend: `python3 urllib.request.urlopen("http://localhost:8000/docs")` - Frontend: `wget --spider -q http://127.0.0.1:3000/` (explicit `127.0.0.1`, not `localhost` — Alpine resolves to IPv6 `::1`) ### Sync Routing Bug Fix - [x] Fixed sync section only showing MDF (not SeriousLetter) - [x] Root cause (two-part): 1. `registry.py` had `desc.sync.get("type") == "cli"` — SL had `sync.type: toolkit`, evaluated to `False` 2. SL's `toolkit` type was itself wrong — should be `cli` with a CLI path - [x] Fix in `registry.py`: `"has_cli": desc.sync.get("type") == "cli"` → `"has_cli": bool(desc.sync.get("type"))` - [x] Fix in `/opt/data/seriousletter/project.yaml`: `sync.type: toolkit` → `type: cli` with `cli:` path ### Backup Date Inconsistency Fix - [x] Fixed overview card showing stale "INT Latest" date while drill-down showed correct newer backups - [x] Root cause: string comparison between incompatible date formats: - Compact (MDF CLI): `20260220_195300` - ISO (toolkit): `2026-02-24T03:00:42` - Character `'0' > '-'` meant compact dates always "won" the `>` comparison - [x] Fix: added `normalizeBackupDate()` function to convert all dates to ISO format at merge time in `mergeBackups()` ## Key Decisions / Learnings - When adding a container to a new network, an ad-hoc `docker network connect` is lost on restart — the fix must go in the compose file - Alpine `localhost` resolves to `::1` (IPv6). Services binding only IPv4 `0.0.0.0` won't respond. Use `127.0.0.1` explicitly in health checks. - For `has_cli` logic: any truthy `sync.type` value means the project has ops CLI support — don't compare to a specific string - Date normalization must happen at merge time, not display time, to get correct `max()` comparisons ## Files Changed - `static/js/app.js` — removed recreate modal/handler, added backup modal, URL routing for restore button, cache invalidation, `normalizeBackupDate()` + `mergeBackups()` fix - `app/routers/registry.py` — `has_cli` logic fix - `/opt/data/seriousletter/project.yaml` — `sync.type` corrected - `/opt/data/seriousletter/{prod,int,dev}/code/docker-compose.yaml` — proxy network + health checks --- **Tags:** #Session #OpsDashboard #BugFix