Jul 14 - Using CDN - Phase 4a
This commit is contained in:
@@ -1383,10 +1383,23 @@ timeout = 30
|
||||
- [ ] Gate: script exits 0 → every local file verified in R2. Cross-check `verified >= referenced-present` from the Phase 0 baseline. Only then flip in Phase 4.
|
||||
|
||||
### Phase 4 — Cutover + iOS
|
||||
- [ ] Maintenance window: final incremental sync → set `STORAGE_BACKEND=s3` → `systemctl restart janitorial-qc`.
|
||||
- [ ] Smoke test: existing web issue/inspection photos load via presigned URLs; new upload from **web** and **iPad** lands in R2.
|
||||
- [ ] iOS: add absolute `photo_url` to issue/inspection payloads; iPad prefers `photo_url`, falls back to `ServerConfig.current + "/static/" + path` (backward-compatible). Record as an iOS CLAUDE.md rule + `APIAssignedIssue`/`APIIssueDetail` field.
|
||||
- [ ] Keep `static/uploads/` for 30 days as backup. Snapshot, then reclaim disk as a **separate, deliberate** step — never part of the migration.
|
||||
**4a — server photo URLs — ✅ delivered:**
|
||||
- [x] `storage.media_url(key, external=False)` — `external=True` yields an absolute URL for off-origin (iPad) consumers: local backend uses `url_for('static', ..., _external=True)`; s3 returns the (already absolute) presigned URL. Templates call with `external=False` → unchanged relative `/static/` URLs.
|
||||
- [x] Issue payload (`api/issues.py` `_issue_payload`) adds `photo_urls` (absolute; order = `[photo_path] + mobile_photo_paths`, matching the iPad's evidence merge) and `result_photo_urls`. Relative keys stay as keys (they're still submission values). Helper `_photo_urls()`.
|
||||
- [x] Inspection detail payload (`api/inspections.py`) adds `form_media` = `{field_id: absolute_url}` for `uploads/...` image field values. Helper `_media()`.
|
||||
- These are additive — the iPad ignores them until 4b ships, and on `local` they're just absolute static URLs, so nothing breaks.
|
||||
|
||||
**4b — iOS: consume the URLs (do before new post-cutover photos appear):**
|
||||
- [ ] Add `photoUrls` / `resultPhotoUrls` to `APIAssignedIssue`; `resultPhotoUrls` to `APIIssueDetail`; `formMedia` to the inspection detail model.
|
||||
- [ ] Store parallel URL arrays on `LocalIssue` (`photoServerUrls`, `resultPhotoServerUrls`), mapped in `pullAssignedIssues` + `refreshStatusFromServer`.
|
||||
- [ ] A resolver that prefers the absolute URL and falls back to `ServerConfig.current + "/static/" + path` (older server / empty URL). Wire the 4 display sites: `IssuesView` (evidence ×1, result ×1), `InspectionHistoryView` (form image; use `formMedia[fieldId]`).
|
||||
- Grace period: local files are kept 30 days post-cutover, and the iPad caches downloaded photos, so old `/static/` URLs keep working during rollout; 4b must land before the 30-day local cleanup and before users need *new* post-cutover photos.
|
||||
|
||||
**Operator cutover runbook (after 4b ships + Phase 3 sync exits clean):**
|
||||
- [ ] Confirm `python scripts/migrate_photos_to_r2.py` prints "✅ SAFE" (0 mismatches) and verified count ≥ Phase 0 baseline present-count.
|
||||
- [ ] Maintenance window: run the sync once more (delta) → set `STORAGE_BACKEND=s3` in `.env` → `systemctl restart janitorial-qc`.
|
||||
- [ ] Smoke test: existing web issue/inspection photos load; existing iPad issue photos load; a **new** upload from web and from iPad lands in R2 and renders; generate an inspection PDF + issue PDF with photos.
|
||||
- [ ] Keep `static/uploads/` for 30 days as backup (untouched). Snapshot, then reclaim disk as a **separate, deliberate** step.
|
||||
|
||||
### Rollback (any phase after cutover)
|
||||
- [ ] `STORAGE_BACKEND=local` → restart. Instant revert; local files were never touched.
|
||||
|
||||
Reference in New Issue
Block a user