101 lines
7.6 KiB
Markdown
101 lines
7.6 KiB
Markdown
# CLAUDE.md — KMountain Flower Radio Station
|
|
|
|
## Project overview
|
|
|
|
A three-tier web app for a community radio station: Angular 21 frontend, FastAPI backend, PostgreSQL database. Renders a hero page, about page, broadcast schedule, upcoming events, and a contact form — styled with a warm mountain/nature color palette (blue primary + orange accent on cream). Donations route to an externally configured URL.
|
|
|
|
## Code style
|
|
|
|
- **Components:** Standalone only (no NgModules). Every component is a single file with `@Component` decorator, `imports` array, and four standard files (`.ts`, `.html`, `.scss`, component).
|
|
- **Data patterns:** Page content (programs, events) is stored in PostgreSQL and served via the FastAPI backend. The frontend uses HTTP services ([src/app/services/](src/app/services/)) to fetch data at runtime. To add or modify content, interact with the API or run `seed.py` — never hardcode content in components.
|
|
- **SCSS:** All design tokens go in [src/styles/_variables.scss](src/styles/_variables.scss). Mixins in [_mixins.scss](src/styles/_mixins.scss). CSS custom properties exposed in [_themes.scss](src/styles/_themes.scss). Components import via `@use 'variables'` (no relative path needed).
|
|
- **Routes:** Defined in [src/app/app.routes.ts](src/app/app.routes.ts) using `loadComponent()` lazy loading. Add new pages here after creating the component.
|
|
- **TypeScript:** Strict mode enabled. Use `readonly` for constants (config, URLs). Keep data shapes close to their components where used only once.
|
|
|
|
## Architecture
|
|
|
|
See [README.md](README.md) for a full architecture map. Key files:
|
|
|
|
- `src/app/app.routes.ts` — all frontend routing
|
|
- `src/environments/environment.ts` — dev API URL (`http://localhost:8000`)
|
|
- `src/environments/environment.prod.ts` — prod API URL (`''`, proxied through Nginx)
|
|
- `src/styles/_variables.scss` — color palette, spacing, breakpoints (change these to re-skin)
|
|
- `src/styles/_mixins.scss` — card-style, button-style, responsive, gradient-text, flowery-border, fade-in
|
|
- `src/styles/_themes.scss` — SCSS → CSS custom property mapping
|
|
- `docker-compose.dev.yml` — dev-sidecar compose for VS Code dev container
|
|
- `backend/app/main.py` — FastAPI entry point (CORS, router registration)
|
|
- `backend/app/models.py` — SQLAlchemy ORM (Show, Event, StationConfig)
|
|
|
|
### Admin dashboard — visitor stats (implemented)
|
|
|
|
Nginx access logs → shared volume → FastAPI log parser → PostgreSQL → admin dashboard. No extra services.
|
|
|
|
**How it works:**
|
|
1. Nginx writes structured JSON access logs to **daily log files** on a shared mount: `/logs/access-YYYY-MM-DD.log`. A `map` directive in [nginx.conf.template](nginx.conf.template) extracts the date from the ISO timestamp, so Nginx rotates to a new file at midnight.
|
|
2. FastAPI ([backend/app/log_parser.py](backend/app/log_parser.py)) maintains a **"last parsed date"** (at most yesterday) persisted in PostgreSQL via the `LogParseState` model. On each run it scans `/logs/` for unprocessed daily files, enriches entries with IP geolocation (MaxMind GeoLite2 City), and stores aggregates.
|
|
3. Aggregates are persisted in `visitor_stats_daily` (unique visitors per day) and `visitor_stats_geo` (country/city breakdown) tables.
|
|
4. Admin dashboard (Angular) reads from stats API endpoints in [backend/app/api/stats.py](backend/app/api/stats.py).
|
|
|
|
**Key files:**
|
|
- `backend/app/log_parser.py` — log file parsing + geo enrichment + `process_unparsed_logs()`
|
|
- `backend/app/api/stats.py` — stats API endpoints (summary, timeseries, geo, parse trigger)
|
|
- `backend/app/models.py` — `VisitorStatDaily`, `VisitorStatGeo`, `LogParseState` ORM models
|
|
- `backend/inject_test_logs.py` — test data injector (writes test log entries for yesterday)
|
|
- `nginx.conf.template` — JSON log format (`kmtn_json`) with daily rotation via `$log_date` map
|
|
- `backend/geo/GeoLite2-City.mmdb` — MaxMind geolocation database
|
|
|
|
**Why daily files:** The currently-being-written file is never read — no partial lines, no race conditions, no file locking. Stats have a minimum one-day lag.
|
|
|
|
**Deployment context:** Single-node minikube, `hostPath` PersistentVolume with `ReadWriteMany`, or docker-compose named volume mounted in both pods.
|
|
|
|
**Injecting test data:** Run `backend/inject_test_logs.py` (or the inline docker compose command in README.md) to write test log entries for yesterday, then trigger parsing via `GET /api/parse`.
|
|
|
|
### Admin dashboard — mobile build orchestration (implemented)
|
|
|
|
Admin users can create mobile build requests, upload platform signing files, run preflight checks, trigger remote builds, monitor logs/status, and download generated artifacts.
|
|
|
|
**High-level flow:**
|
|
1. Admin creates a build request (platforms, identifiers, version/build numbers, release profile).
|
|
2. Admin uploads exactly two files per selected platform:
|
|
- Android: keystore + descriptor JSON
|
|
- iOS: `.p12` certificate + `.mobileprovision` profile
|
|
3. Backend preflight validates request completeness, file presence, platform requirements, and basic iOS CarPlay entitlement marker.
|
|
4. Backend dispatches build via SSH to external build workers:
|
|
- Android worker runs `./do_android_build.sh`
|
|
- iOS worker runs `./do_ios_build.sh`
|
|
5. Backend copies generated `.aab`/`.ipa` artifacts back, stores metadata/checksums, and exposes admin download URLs.
|
|
|
|
**SSH contract (current implementation):**
|
|
- Connect as user `buildbot` (password-auth expected in the build-control environment).
|
|
- Remote app directory is `~/mobile_app`.
|
|
- Uploaded signing files are copied into `~/mobile_app` before script execution.
|
|
- Build scripts are executed from `~/mobile_app`.
|
|
|
|
**Key backend files:**
|
|
- [backend/app/api/mobile_builds.py](backend/app/api/mobile_builds.py) — mobile build endpoints, preflight, SSH execution, artifact collection
|
|
- [backend/app/models.py](backend/app/models.py) — `MobileBuildRequest`, `MobileBuildUploadedFile`, `MobileBuildArtifact`
|
|
- [backend/app/schemas.py](backend/app/schemas.py) — mobile build request/response schemas
|
|
- [backend/app/config.py](backend/app/config.py) — `KMTN_MOBILE_BUILD_*` settings (hosts, scripts, paths)
|
|
- [backend/app/main.py](backend/app/main.py) — router registration at `/api/mobile-builds`
|
|
|
|
**Key frontend files:**
|
|
- [src/app/admin/admin.component.ts](src/app/admin/admin.component.ts) — mobile build tab state/actions
|
|
- [src/app/admin/admin.component.html](src/app/admin/admin.component.html) — upload/preflight/trigger/logs/download UI
|
|
- [src/app/admin/admin.component.scss](src/app/admin/admin.component.scss) — mobile build panel styling
|
|
- [src/app/services/mobile-build.service.ts](src/app/services/mobile-build.service.ts) — API client for mobile build endpoints
|
|
- [src/app/interfaces/mobile-build.ts](src/app/interfaces/mobile-build.ts) — frontend build models
|
|
|
|
**Current scope:**
|
|
- Build artifact generation only (`.aab` and `.ipa`).
|
|
- No automatic App Store Connect / Google Play upload.
|
|
|
|
## Common tasks
|
|
|
|
- **Re-skin:** Edit colors in `_variables.scss` only.
|
|
- **Add a page:** Create component folder → add route in `app.routes.ts` → add nav link (if needed).
|
|
- **Update content (dev):** Run the API, then use Swagger UI at [http://localhost:8000/docs](http://localhost:8000/docs) to create/update content.
|
|
- **Update content (seed):** Edit [backend/seed.py](backend/seed.py) and restart the API (autoseed runs on empty DB), or hit `curl -X POST http://localhost:8000/api/admin/reset` to re-seed.
|
|
- **Run the app (dev):** From the `backend/` directory, set `KMTN_DATABASE_URL=sqlite+aiosqlite:///./kmountain.db`, then `uvicorn app.main:app --reload` + `ng serve` in parallel. Autoseed populates data on first run.
|
|
- **Run the app (dev container):** Reopen in Container via VS Code.
|
|
- **Build:** `ng build` → `dist/`.
|