# KMountain Flower Radio Station A three-tier web application for a community radio station: Angular 21 frontend, FastAPI backend, PostgreSQL database. Renders a hero page, about page, broadcast schedule, donation tiers, upcoming events, and a contact form — styled with a warm mountain/nature aesthetic. ## Quick Start ### Option A — Docker Compose (recommended) ```bash # Start all services docker compose up --build # Seed the database (first run only) docker compose exec api python seed.py ``` Open [http://localhost:4200](http://localhost:4200). ### Option B — Manual (frontend + backend separately) ```bash # Database createdb kmountain export KMTN_DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/kmountain # Backend cd backend && pip install -r requirements.txt uvicorn app.main:app --reload # Frontend (new terminal) npm install && ng serve ``` Open [http://localhost:4200](http://localhost:4200) and ensure the backend is running on [http://localhost:8000](http://localhost:8000). ## Prerequisites | Requirement | Version | Notes | |---|---|---| | Docker + Docker Compose | 24+ | For the Docker Compose path | | Node.js | 20+ | For manual frontend development | | npm | 11+ | | | Python | 3.12+ | For manual backend development | | PostgreSQL | 16+ | For manual backend development (Docker handles it) | ## Architecture ``` ┌──────────────┐ │ Browser │ │ :4200 / :80│ └──────┬───────┘ │ ┌────────────┴────────────┐ │ │ ┌────▼─────┐ ┌──────▼─────┐ │ Nginx │ /api/* │ FastAPI │ │ (static) │─ proxy ──▶ │ :8000 │ └────┬─────┘ └──────┬─────┘ │ │ ┌────▼─────┐ ┌──────▼─────┐ │ Angular 21│ │ PostgreSQL │ │ SPA │ │ :5432 │ └──────────┘ └────────────┘ ``` **Three tiers:** 1. **Frontend** — Angular 21 SPA built via multi-stage Dockerfile and served by Nginx. In production, Nginx serves the static files and proxies `/api/*` requests to the backend. In development, both run on separate ports and the browser talks directly to both. 2. **Backend** — Python FastAPI with async SQLAlchemy. Provides CRUD REST endpoints for programs (broadcast schedule), events (upcoming events), and donation tiers. Auto-generates Swagger/OpenAPI docs at `/docs`. 3. **Database** — PostgreSQL 16. Schema contains three tables: `programs`, `events`, `donation_tiers`. Initial data is loaded via [seed.py](backend/seed.py). ## Project Structure ``` . ├── docker-compose.yml # 3-service stack (db, api, frontend) ├── Dockerfile # Multi-stage: Angular build → Nginx serve ├── nginx.conf # Static files + /api/* proxy ├── .env.example # Environment variable template ├── .devcontainer/ # VS Code dev container │ ├── src/ # Angular 21 frontend │ ├── app/ # Standalone components │ │ ├── app.routes.ts # Lazy-loaded route map │ │ ├── hero/ about/ schedule/ donate/ events/ contact/ navbar/ footer/ │ │ ├── services/ # HTTP services (program, event, tier) │ │ └── interfaces/ # TypeScript types (Program, Event, Tier) │ ├── environments/ # dev/prod config (apiBaseUrl) │ └── styles/ # SCSS design tokens │ ├── _variables.scss # Colors, spacing, breakpoints │ ├── _mixins.scss # Card, button, responsive mixins │ └── _themes.scss # SCSS → CSS custom properties │ └── backend/ # FastAPI backend ├── Dockerfile # Python gunicorn/uvicorn ├── requirements.txt ├── seed.py # Initial data for all models └── app/ ├── main.py # FastAPI app, CORS, router registration ├── config.py # ENV-based settings (KMTN_* prefix) ├── database.py # async engine + session factory ├── models.py # SQLAlchemy ORM (Program, Event, DonationTier) ├── schemas.py # Pydantic schemas (Create/Update/Response) └── api/ ├── programs.py # CRUD /api/programs ├── events.py # CRUD /api/events └── tiers.py # CRUD /api/tiers ``` ## Local Development ### Docker Compose ```bash docker compose up --build # Start all services docker compose exec api python seed.py # Seed database (first run) docker compose logs -f api # Stream backend logs docker compose down # Stop and remove containers ``` The `pgdata` volume persists the database across container restarts. ### Manual Development Run all three layers in parallel: ```bash # 1. Database (install PostgreSQL 16 locally) createdb kmountain # 2. Backend (new terminal) cd backend pip install -r requirements.txt export KMTN_DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/kmountain export KMTN_CORS_ORIGINS='["http://localhost:4200"]' uvicorn app.main:app --reload # 3. Frontend (another terminal) npm install ng serve ``` ## Configuration | Variable | Default | Where | Description | |---|---|---|---| | `KMTN_DATABASE_URL` | `postgresql+asyncpg://postgres:postgres@localhost:5432/kmountain` | [backend/app/config.py](backend/app/config.py) | Async PostgreSQL connection string | | `KMTN_CORS_ORIGINS` | `["http://localhost:4200"]` | [backend/app/config.py](backend/app/config.py) | CORS allowlist (JSON array) | | `apiBaseUrl` | `http://localhost:8000` (dev) / `''` (prod) | [src/environments/](src/environments/) | Where the Angular app finds the API | Environment variables are prefixed with `KMTN_` in the backend. For production, set `KMTN_DATABASE_URL` to your managed PostgreSQL connection and update `KMTN_CORS_ORIGINS` to your actual domain. ## Building ```bash # Frontend (produces dist/radio-station/browser/) ng build --configuration production # Backend Docker image docker build -t kmountain-api -f backend/Dockerfile backend/ # Full production stack docker compose up --build -d ``` ## Testing ```bash ng test # Angular + Vitest ``` The backend API is self-documented at [http://localhost:8000/docs](http://localhost:8000/docs) (Swagger UI). Run `docker compose exec api python seed.py` and visit that URL to interactively test endpoints. ## API Reference The backend exposes CRUD endpoints for three models: | Method | Endpoint | Description | |---|---|---| | `GET` | `/api/programs` | List all programs (`?day=1..7` to filter by day of week) | | `GET` | `/api/programs/:id` | Single program | | `POST` | `/api/programs` | Create program | | `PUT` | `/api/programs/:id` | Update program | | `DELETE` | `/api/programs/:id` | Delete program | | `GET` | `/api/events` | List events (`?active=true` to filter active events) | | `GET` | `/api/events/:id` | Single event | | `POST` | `/api/events` | Create event | | `PUT` | `/api/events/:id` | Update event | | `DELETE` | `/api/events/:id` | Delete event | | `GET` | `/api/tiers` | List donation tiers (ordered by `display_order`) | | `GET` | `/api/tiers/:id` | Single tier | | `POST` | `/api/tiers` | Create tier | | `PUT` | `/api/tiers/:id` | Update tier | | `DELETE` | `/api/tiers/:id` | Delete tier | Full interactive API docs at [http://localhost:8000/docs](http://localhost:8000/docs). ## Deployment ### Production (Docker Compose on a VPS) ```bash # 1. Clone repo on server git clone kmountain cd kmountain # 2. Edit docker-compose.yml # - Change POSTGRES_PASSWORD to a strong value # - Update KMTN_CORS_ORIGINS to your domain # - Optionally set KMTN_DATABASE_URL to a managed PostgreSQL # 3. Build and start docker compose up --build -d # 4. Seed the database docker compose exec api python seed.py ``` ### Production hardening checklist - [ ] Change the default `POSTGRES_PASSWORD` in [docker-compose.yml](docker-compose.yml) - [ ] Update `KMTN_CORS_ORIGINS` to your actual domain (not `localhost`) - [ ] Set `KMTN_DATABASE_URL` to a managed PostgreSQL connection (RDS, Cloud SQL, etc.) - [ ] Add a reverse proxy (Caddy, Traefik) in front of Nginx for HTTPS - [ ] Set up log rotation for Docker containers - [ ] Configure Docker image registry and push/pull workflow ## Development Tools - **Dev container** — Open the repo in VS Code → "Reopen in Container" for a pre-configured environment with Angular CLI, Node.js, Python 3, and Git ([.devcontainer/](.devcontainer/)). - **VS Code workspace** — [workspace file](web_app.code-workspace) in the repo root configures both Angular and Python extensions. - **Prettier** — Run `npx prettier --write .` to format the codebase. - **Prettier config** — [**.prettierrc**](.prettierrc) (100 char width, single quotes, Angular HTML parser). ## Tech Stack | Layer | Technology | |---|---| | Frontend | Angular 21 (standalone components, signals-ready) | | Backend | FastAPI 0.115, Python 3.12 | | Database | PostgreSQL 16 (async via SQLAlchemy 2.0) | | Styling | SCSS with design tokens (variables → mixins → CSS custom properties) | | Routing | Angular Router (lazy loaded) + Nginx SPA fallback | | HTTP | RxJS observables (frontend → FastAPI) | | Containers | Docker Compose (3 services: db, api, frontend) | | CI/CD | _(not yet configured)_ | | Testing | Vitest (via Angular CLI) |