7.2 KiB
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
@Componentdecorator,importsarray, 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/) 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. Mixins in _mixins.scss. CSS custom properties exposed in _themes.scss. Components import via
@use 'variables'(no relative path needed). - Routes: Defined in src/app/app.routes.ts using
loadComponent()lazy loading. Add new pages here after creating the component. - TypeScript: Strict mode enabled. Use
readonlyfor constants (config, URLs). Keep data shapes close to their components where used only once.
Architecture
See README.md for a full architecture map. Key files:
src/app/app.routes.ts— all frontend routingsrc/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-insrc/styles/_themes.scss— SCSS → CSS custom property mappingdocker-compose.dev.yml— dev-sidecar compose for VS Code dev containerbackend/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:
- Nginx writes structured JSON access logs to daily log files on a shared mount:
/logs/access-YYYY-MM-DD.log. Amapdirective in nginx.conf.template extracts the date from the ISO timestamp, so Nginx rotates to a new file at midnight. - FastAPI (backend/app/log_parser.py) maintains a "last parsed date" (at most yesterday) persisted in PostgreSQL via the
LogParseStatemodel. On each run it scans/logs/for unprocessed daily files, enriches entries with IP geolocation (MaxMind GeoLite2 City), and stores aggregates. - Aggregates are persisted in
visitor_stats_daily(unique visitors per day) andvisitor_stats_geo(country/city breakdown) tables. - Admin dashboard (Angular) reads from stats API endpoints in 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,LogParseStateORM modelsbackend/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_datemapbackend/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:
- Admin creates a build request (platforms, identifiers, version/build numbers, release profile).
- Admin uploads exactly two files per selected platform:
- Android: keystore + descriptor JSON
- iOS:
.p12certificate +.mobileprovisionprofile
- Backend preflight validates request completeness, file presence, platform requirements, and basic iOS CarPlay entitlement marker.
- Backend dispatches build via SSH to external build workers:
- Android worker runs
./do_android_build.sh - iOS worker runs
./do_ios_build.sh
- Android worker runs
- Backend copies generated
.aab/.ipaartifacts 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_appbefore script execution. - Build scripts are executed from
~/mobile_app.
Key backend files:
- backend/app/api/mobile_builds.py — mobile build endpoints, preflight, SSH execution, artifact collection
- backend/app/models.py —
MobileBuildRequest,MobileBuildUploadedFile,MobileBuildArtifact - backend/app/schemas.py — mobile build request/response schemas
- backend/app/config.py —
KMTN_MOBILE_BUILD_*settings (hosts, scripts, paths) - backend/app/main.py — router registration at
/api/mobile-builds
Key frontend files:
- src/app/admin/admin.component.ts — mobile build tab state/actions
- src/app/admin/admin.component.html — upload/preflight/trigger/logs/download UI
- src/app/admin/admin.component.scss — mobile build panel styling
- src/app/services/mobile-build.service.ts — API client for mobile build endpoints
- src/app/interfaces/mobile-build.ts — frontend build models
Current scope:
- Build artifact generation only (
.aaband.ipa). - No automatic App Store Connect / Google Play upload.
Deployment
The project uses a single parameterized docker-compose.yml driven by customer-specific .env files in the customers/ directory.
Deploy all customers:
./deploy-all.sh
Deploy a specific customer:
docker compose --env-file customers/.env.<customer_name> -p <customer_name> up -d --build
Note: Multiple legacy docker-compose.yml files have been removed in favor of this template system.