Files
kmtnflower/.claude/CLAUDE.md
T
2026-07-17 16:27:06 -07:00

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 @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/) 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 readonly for 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 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 extracts the date from the ISO timestamp, so Nginx rotates to a new file at midnight.
  2. FastAPI (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.

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.pyVisitorStatDaily, 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:

Key frontend files:

Current scope:

  • Build artifact generation only (.aab and .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.