Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
|
Download docs/ARCHITECTURE.md from VAILL/legislation-tracker: direct link, hf CLI and curl.
- Browser
- Download file 5.53 kB
-
https://huggingface.co/spaces/VAILL/legislation-tracker/resolve/main/docs/ARCHITECTURE.md
- Command line
-
hf download hf://spaces/VAILL/legislation-tracker/docs/ARCHITECTURE.md
-
curl -L -o ARCHITECTURE.md https://huggingface.co/spaces/VAILL/legislation-tracker/resolve/main/docs/ARCHITECTURE.md
5.53 kB
| # Architecture | |
| ## High-level | |
| ``` | |
| HuggingFace Docker Space (single container, port 7860) | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Browser ββHTTPSβββΆ β Next.js 15 ββ /api/* rewrite βββΆ FastAPI (:8000) β | |
| β (port 7860) β β | |
| β ββ repos/ ββββ β | |
| β supervisord runs: ββ services/ β β | |
| β β frontend (node server.js) ββ routers/ β β | |
| β β backend (uvicorn) β β | |
| β β scheduler (weekly cron) β β | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββ | |
| β | |
| HuggingFace Dataset (source of truth) | |
| VAILL/Legislation-Tracker-Data | |
| β | |
| On container start: sync ββββββββ | |
| On pipeline run: upload βββββββββΆ | |
| ``` | |
| ## Components | |
| ### Frontend β `frontend/` | |
| - Next.js 15 App Router, TypeScript, Tailwind CSS. | |
| - Each public page has a matching `loading.tsx` that renders skeletons while | |
| the server component fetches (Next.js 15 Suspense route conventions). | |
| - Client components (Bills table, Calendar, Newsletter, State Spotlight, | |
| USMap) fetch from the browser to `/api/*`, which is same-origin β the | |
| Next.js rewrite in [`next.config.ts`](../frontend/next.config.ts) proxies | |
| those to the internal FastAPI port. | |
| - Server components use an absolute internal URL (`http://127.0.0.1:8000`) | |
| because Node's `fetch` can't resolve relative paths. | |
| ### Backend β `backend/app/` | |
| - FastAPI, Pydantic v2. | |
| - Layered: | |
| - `routers/` β HTTP endpoints, thin. | |
| - `repositories/` β data access. Reads from `data/*.json` (mirrored from | |
| the HF Dataset). Designed so a future Postgres swap only touches this | |
| layer. | |
| - `services/` β external side-effects (`hf_sync.py`, `eu_vectorstore.py`). | |
| - `schemas/` β Pydantic response models. | |
| - `auth/` β file-based bcrypt, JWT sessions. | |
| ### Pipeline β `backend/data_updating_scripts/` + `backend/update_data.py` | |
| - 14 sequential Python scripts. See [PIPELINE.md](PIPELINE.md). | |
| - `update_data.py` orchestrates: reads args, invokes each step as a | |
| subprocess, tracks status in `data/pipeline_status.json`, uploads back to | |
| HF Dataset when done (unless `--skip-upload`). | |
| ### Scheduler β `backend/scheduler.py` | |
| - APScheduler `BlockingScheduler` on `America/Chicago`. | |
| - Runs `update_data.py --pull --overwrite-pdf --continue-on-error` every | |
| Monday at 7:00 AM CT. | |
| - Only starts inside the Docker container (via supervisord). Local dev does | |
| not spawn a scheduler by default. | |
| ## Request lifecycle | |
| A browser request to https://vaill-legislation-tracker.hf.space: | |
| 1. HF's edge routes to the Space's container on port 7860 (Next.js). | |
| 2. If the URL is a page (e.g. `/bills`), Next.js renders it β the server | |
| component fetches data from `http://127.0.0.1:8000/api/*` inside the | |
| container. | |
| 3. If the URL is `/api/*`, the Next.js server rewrite proxies to FastAPI | |
| internally. | |
| 4. FastAPI hits the in-memory repository, which was populated from `data/*` | |
| at startup (which was synced from the HF Dataset). | |
| 5. Response goes back through Next.js β HF edge β browser. | |
| ## Startup sequence (container boot) | |
| 1. supervisord launches three programs: backend, frontend, scheduler. | |
| 2. Backend runs `app.main` on import: | |
| - Loads settings from environment (HF Space secrets). | |
| - If `HF_REPO_ID` and `HUGGINGFACE_HUB_TOKEN` are set, syncs all core | |
| JSONs + `weekly_changes/` + `newsletter_drafts/` from the HF Dataset. | |
| `snapshots/` is intentionally excluded (large + only needed at | |
| pipeline time). | |
| - If `OPENAI_API_KEY` is set and no EU vectorstore exists on disk, | |
| builds it from the bundled `eu-ai-act.pdf` (~30s, ~$0.001). | |
| - Eagerly loads bills into memory. | |
| - Registers routers and starts serving. | |
| 3. Frontend runs `node server.js` on port 7860 (Next.js standalone). | |
| 4. Scheduler stays idle until its cron fires. | |
| ## Data storage | |
| - **HF Dataset** β source of truth. Any code path that persists data | |
| ultimately writes here. See [DATA.md](DATA.md). | |
| - **Container disk** (`data/`) β a cached mirror. Ephemeral on HF; wiped on | |
| every container restart and refilled from HF. | |
| - **Local `.env` files** β dev secrets only. Git-ignored. | |
| - **HF Space secrets** β production values for API keys, HF token, | |
| SendGrid, etc. Set in the Space Settings, exposed as environment | |
| variables to the container. | |
| ## Authentication | |
| - Users in `users.json` (synced from HF). | |
| - Login POSTs `username + password` to `/api/auth/login`. Backend | |
| bcrypt-checks against the hash and returns a signed JWT. | |
| - Frontend stores the token in `localStorage` (`admin_token`) and sends it | |
| as `Authorization: Bearer β¦` on `/api/admin/*` requests. | |
| - Admin routes gate on `require_admin` FastAPI dependency. | |