Panoramax2 - #107
Conversation
…ew/1/hillview into panoramax2 # Conflicts: # CLAUDE.md # backend/uv.lock
There was a problem hiding this comment.
🟡 Changes recommended
A few configuration/robustness issues (URL-prefix normalization for self-dedup, required DB password gating for the optional compose profile, clearer scope validation) should be addressed to avoid incorrect filtering and operational failure modes.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds first-class support for Hillview participating in the Panoramax federation by introducing a dedicated read-only(ish) Panoramax/GeoVisio-compatible STAC API service, backed by a new panoramax DB schema and a sequencer that synthesizes per-owner time-gap photo sequences. It also updates the frontend Panoramax source to filter out “self” items when the federation meta-catalog reflects Hillview’s own photos back into the Panoramax feed, and documents/automates verification (unit + full harvester E2E).
Changes:
- Add
backend/panoramax/service (FastAPI STAC API + sequencer + CQL2 subset parsing) and wire it into Docker Compose via an opt-inpanoramaxprofile. - Add Alembic migration
030_add_panoramax_schemacreatingpanoramax.sequences/panoramax.sequence_photosplus triggers to drive incremental-harvest updates and tombstones. - Add frontend self-duplicate detection (
isOwnInstanceItem) with unit tests and new config knobs for own instance/asset URL matching.
File summaries
| File | Description |
|---|---|
| frontend/src/lib/sources/PanoramaxSourceLoader.ts | Adds self-item detection to drop Hillview-origin items from federated Panoramax results. |
| frontend/src/lib/sources/PanoramaxSourceLoader.test.ts | Adds unit tests for isOwnInstanceItem behavior and edge cases. |
| frontend/src/lib/config.ts | Introduces configurable lists for own Panoramax instance URLs and asset host prefixes. |
| docs/panoramax-federation.md | Comprehensive design/ops documentation for federation participation and verification. |
| docker/postgres/initdb.d/create-panoramax-role.sh | Creates panoramax_ro role on fresh clusters when enabled via env. |
| docker-compose.yml | Adds PANORAMAX_DB_PASSWORD plumbing and a new panoramax service/profile. |
| docker-compose.dev.yml | Adds dev override for the panoramax service (host networking + live-mounted app). |
| CLAUDE.md | Links the new federation documentation from the repo’s primary index. |
| backend/uv.lock | Adds the new hillview-panoramax workspace package and its dependencies. |
| backend/scripts/provision_panoramax_role.sh | Adds an idempotent role+grants provisioning script for existing clusters. |
| backend/pyproject.toml | Adds panoramax as a uv workspace member. |
| backend/panoramax/scripts/seed_photos.py | Seeds CC photos through the real upload pipeline for E2E federation tests. |
| backend/panoramax/scripts/e2e_federation.sh | End-to-end script that drives the real meta-catalog harvester against this repo. |
| backend/panoramax/run_unit_tests.sh | Adds a unit-test runner for the panoramax service package. |
| backend/panoramax/pyproject.toml | Defines the hillview-panoramax Python package and dependencies. |
| backend/panoramax/Dockerfile | Container build for the Panoramax STAC API service. |
| backend/panoramax/app/tests/unit/test_stac.py | Unit tests for STAC serialization, datetime formatting, asset selection, tombstones. |
| backend/panoramax/app/tests/unit/test_sequencer_logic.py | Unit tests for sequencer core logic (session splitting, ID assignment, diffs). |
| backend/panoramax/app/tests/unit/test_cql.py | Unit tests for the accepted CQL2-text filter subset and rejection behavior. |
| backend/panoramax/app/stac.py | Implements STAC Collection/Item serialization and asset mapping to GeoVisio conventions. |
| backend/panoramax/app/settings.py | Panoramax service configuration (scope, base URL, viewer URL, paging limits). |
| backend/panoramax/app/sequencer.py | Implements deterministic recompute + diffed persistence of sequences/memberships. |
| backend/panoramax/app/main.py | Implements the Panoramax/GeoVisio-compatible read API endpoints consumed by the harvester. |
| backend/panoramax/app/eligibility.py | Centralizes federation eligibility SQL predicate for both sequencer + serve-time filtering. |
| backend/panoramax/app/db.py | Async SQLAlchemy engine setup for the dedicated panoramax container. |
| backend/panoramax/app/cql.py | Parses and enforces a strict subset of CQL2-text for incremental harvest filtering. |
| backend/panoramax/app/init.py | Package marker for the panoramax service. |
| backend/api/app/alembic/versions/030_add_panoramax_schema.py | Adds the panoramax schema, tables, triggers, and conditional grants. |
| .env.example | Documents env vars for enabling/configuring the panoramax federation service. |
Review details
- Files reviewed: 27/29 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const parseUrlList = (raw: unknown, fallback: string[]): string[] => | ||
| typeof raw === 'string' && raw.trim() | ||
| ? raw.split(',').map((u) => u.trim()).filter(Boolean) | ||
| : fallback; | ||
| export const ownPanoramaxInstanceUrls = parseUrlList( | ||
| import.meta.env.VITE_OWN_PANORAMAX_INSTANCE_URLS, | ||
| ['https://panoramax.hillview.cz'] | ||
| ); | ||
| export const ownPhotoAssetUrlPrefixes = parseUrlList( | ||
| import.meta.env.VITE_OWN_PHOTO_ASSET_URL_PREFIXES, | ||
| ['https://pics.hillview.cz/', 'https://pics2.hillview.cz/', 'https://pics4.t3.storage.dev/'] | ||
| ); |
| ports: | ||
| - "127.0.0.1:8058:8058" | ||
| environment: | ||
| - DATABASE_URL=postgresql+asyncpg://panoramax_ro:${PANORAMAX_DB_PASSWORD:-}@${postgres:-postgres}:5432/${POSTGRES_DB:?POSTGRES_DB is required} | ||
| # Public canonical base URL registered in the meta-catalog. Its last |
| panoramax: | ||
| network_mode: host | ||
| environment: | ||
| - DATABASE_URL=postgresql+asyncpg://panoramax_ro:${PANORAMAX_DB_PASSWORD:-}@localhost:${POSTGRES_HOST_PORT:-25432}/${POSTGRES_DB} | ||
| volumes: | ||
| - ./backend/panoramax/app:/app/app |
| def active_scope() -> Scope: | ||
| return SCOPES[os.getenv('PANORAMAX_SCOPE', 'cc')] | ||
|
|
…ew/1/hillview into panoramax2
No description provided.