working 3 tier approach
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
# Backend Infrastructure for KMountain Flower Radio Station
|
||||
|
||||
## Context
|
||||
|
||||
The app currently has a polished frontend with zero backend — all dynamic content (schedule, events, donations) lives as hardcoded `readonly` arrays in components. The contact form just shows an alert. We need to add persistent data, a backend API, and Docker-based deployment while keeping things simple for a volunteer-run community radio station.
|
||||
|
||||
## VS Code Workspace Setup
|
||||
|
||||
Use a **multi-root workspace** so both projects get their own IDE context, linting, and debugging configs:
|
||||
|
||||
```
|
||||
web_app/ ← repo root (existing)
|
||||
├── .vscode/
|
||||
│ ├── settings.json ← workspace settings (shared)
|
||||
│ ├── tasks.json ← common tasks (start backend, start frontend)
|
||||
│ └── launch.json ← debug configs for both projects
|
||||
├── .vscode/workspace.code-workspace ← multi-root workspace file (open this in VS Code)
|
||||
├── src/ ← existing Angular frontend (unchanged structure)
|
||||
├── backend/ ← new FastAPI backend
|
||||
│ ├── .vscode/
|
||||
│ │ ├── settings.json ← Python/Flake8/Pylance settings
|
||||
│ │ └── launch.json ← FastAPI debug config
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile ← Angular build + nginx serve (at repo root)
|
||||
└── nginx.conf ← nginx config (at repo root)
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Create `web_app/.vscode/` directory with shared workspace settings
|
||||
2. Create `web_app/.vscode/workspace.code-workspace` listing both roots:
|
||||
```json
|
||||
{
|
||||
"folders": [
|
||||
{ "path": "." },
|
||||
{ "path": "backend" }
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
```
|
||||
3. VS Code: File → Open Workspace from File → select `workspace.code-workspace`
|
||||
4. Backend folder gets Python language server; frontend folder gets Angular TS language server — no conflict
|
||||
|
||||
## Decisions (confirmed)
|
||||
|
||||
- **Admin panel**: Skip for now. Initial data via a seed script.
|
||||
- **Contact form**: Leave as-is (alert only, unimplemented for now).
|
||||
- **Rollout**: Phase by phase.
|
||||
- **Backend**: FastAPI (Python 3 already in devcontainer, auto-docs, async).
|
||||
- **Database**: PostgreSQL in Docker (production-ready, any managed host compatible).
|
||||
- **Frontend serve**: nginx in Docker (build Angular → serve static).
|
||||
|
||||
## Target Architecture
|
||||
|
||||
```
|
||||
docker-compose.yml
|
||||
├── db (postgres:16-alpine) -- data tier, persistent volume
|
||||
├── api (FastAPI + uvicorn) -- middleware tier, CORS + all endpoints
|
||||
└── frontend (nginx) -- frontend tier, Angular build + API proxy in prod
|
||||
```
|
||||
|
||||
## Phase 1: Backend + Schedule Page + Docker Skeleton
|
||||
|
||||
### 1.0 VS Code workspace setup
|
||||
|
||||
1. Create `.vscode/` directory with `workspace.code-workspace`, `settings.json`, `tasks.json`, `launch.json`
|
||||
2. Create `backend/.vscode/settings.json` (Python language settings)
|
||||
3. Open `workspace.code-workspace` in VS Code — both roots visible, each with its own language server
|
||||
4. `tasks.json` provides unified commands: "Start Backend" (uvicorn --reload), "Start Frontend" (ng serve), "Docker Up"
|
||||
|
||||
### 1.1 Backend skeleton
|
||||
|
||||
Create `backend/` directory:
|
||||
|
||||
```
|
||||
backend/
|
||||
├── requirements.txt -- fastapi, uvicorn, psycopg2-binary, alembic, python-multipart
|
||||
├── Dockerfile -- python:3.12-slim, copy requirements, copy app, CMD uvicorn
|
||||
├── seed.py -- seed 10 weekday programs
|
||||
└── app/
|
||||
├── __init__.py
|
||||
├── main.py -- FastAPI app, lifespan (DB init), CORS middleware
|
||||
├── config.py -- DATABASE_URL, CORS_ORIGINS from env vars
|
||||
├── database.py -- async SessionLocal engine
|
||||
├── models.py -- Program SQLAlchemy model
|
||||
├── schemas.py -- Program Pydantic schemas (read + create + update)
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── programs.py -- GET /api/programs (with ?day filter), POST, PUT, DELETE
|
||||
```
|
||||
|
||||
**Program model**: `id, day_of_week (int), day_label (str), time (str), title (str), host (str), genre (str)`
|
||||
|
||||
**Seed data**: The 10 existing weekday programs from [schedule.component.ts:19-29](src/app/schedule/schedule.component.ts).
|
||||
|
||||
### 1.2 Angular service layer
|
||||
|
||||
Modify:
|
||||
- [src/app/app.config.ts](src/app/app.config.ts) -- add `provideHttpClient()` to providers
|
||||
- [src/environments/environment.ts](src/environments/environment.ts) -- **new**: `apiBaseUrl` for dev/prod
|
||||
|
||||
Create:
|
||||
- [src/app/interfaces/program.ts](src/app/interfaces/program.ts) -- `Program` interface mirroring backend
|
||||
- [src/app/services/program.service.ts](src/app/services/program.service.ts) -- `getPrograms()`, `getProgram()`, `createProgram()`, etc.
|
||||
|
||||
### 1.3 Convert schedule component
|
||||
|
||||
Modify [src/app/schedule/schedule.component.ts](src/app/schedule/schedule.component.ts):
|
||||
- Remove `readonly programs: Program[]` array
|
||||
- `inject(ProgramService)` in class body
|
||||
- Add `programs: Program[] = []` and `loading = true`
|
||||
- Load data in `ngOnInit()` via service
|
||||
- Template: add loading/empty states, keep `*ngFor` structure
|
||||
|
||||
### 1.4 Docker skeleton
|
||||
|
||||
Create:
|
||||
- [docker-compose.yml](docker-compose.yml) -- db + api services, healthcheck on db, depends_on ordering, named `pgdata` volume
|
||||
- [backend/Dockerfile](backend/Dockerfile) -- already covered in 1.1
|
||||
|
||||
### Phase 1 verification
|
||||
|
||||
| Check | How |
|
||||
|-------|-----|
|
||||
| Backend runs locally | `cd backend && uvicorn app.main:app --reload` |
|
||||
| OpenAPI docs at `/docs` | Browse `http://localhost:8000/docs` |
|
||||
| Seed data present | `curl http://localhost:8000/api/programs` returns 10 items |
|
||||
| Schedule page fetches from DB | DevTools Network tab shows GET to `/api/programs`, page renders |
|
||||
| Docker stack | `docker compose up` -- db + api running, schedule renders on frontend |
|
||||
|
||||
## Phase 2: Donate + Events Pages
|
||||
|
||||
### 2.1 Backend additions
|
||||
|
||||
Modify `backend/app/`:
|
||||
- [models.py](backend/app/models.py) -- add `Event` and `DonationTier` models
|
||||
- Event: `id, date (DATE), title, description (TEXT), location, icon, rsvp_url (nullable), active (bool)`
|
||||
- DonationTier: `id, name (UNIQUE), amount (NUMERIC), icon, benefits (TEXT[]), display_order (int)`
|
||||
- [schemas.py](backend/app/schemas.py) -- add Pydantic schemas for both
|
||||
- [api/events.py](backend/app/api/events.py) -- GET/POST/PUT/DELETE with `?active` filter
|
||||
- [api/tiers.py](backend/app/api/tiers.py) -- GET/POST/PUT/DELETE ordered by `display_order`
|
||||
- [seed.py](backend/seed.py) -- add events and tiers data (from [events.component.ts](src/app/events/events.component.ts) and [donate.component.ts](src/app/donate/donate.component.ts))
|
||||
|
||||
### 2.2 Angular additions
|
||||
|
||||
Create:
|
||||
- [src/app/interfaces/event.ts](src/app/interfaces/event.ts)
|
||||
- [src/app/interfaces/tier.ts](src/app/interfaces/tier.ts)
|
||||
- [src/app/services/event.service.ts](src/app/services/event.service.ts) -- `getEvents()`, `createEvent()`, etc.
|
||||
- [src/app/services/tier.service.ts](src/app/services/tier.service.ts) -- `getTiers()` ordered
|
||||
|
||||
Modify:
|
||||
- [src/app/events/events.component.ts](src/app/events/events.component.ts) -- remove readonly array, inject EventService, load in ngOnInit
|
||||
- [src/app/donate/donate.component.ts](src/app/donate/donate.component.ts) -- remove readonly array, inject TierService, load in ngOnInit
|
||||
- Keep template structure identical, just swap the data source
|
||||
|
||||
### Phase 2 verification
|
||||
|
||||
| Check | How |
|
||||
|-------|-----|
|
||||
| Events API | `curl http://localhost:8000/api/events?active=true` returns 4 events |
|
||||
| Tiers API | `curl http://localhost:8000/api/tiers` returns 4 tiers ordered |
|
||||
| Events page renders from DB | Navigate to /events, verify from DB not hardcoded |
|
||||
| Donate page renders from DB | Navigate to /donate, verify from DB not hardcoded |
|
||||
|
||||
## Phase 3: Frontend Docker + Full Stack
|
||||
|
||||
### 3.1 Dockerfile + nginx config
|
||||
|
||||
Create at repo root alongside `src/` and `backend/`:
|
||||
|
||||
- `Dockerfile` -- multi-stage: node:20-alpine build Angular → nginx:alpine serve the dist output
|
||||
- `nginx.conf` -- serve Angular static files; in production mode, proxy `/api/*` to api service (avoids CORS in prod)
|
||||
|
||||
### 3.2 Update docker-compose.yml
|
||||
|
||||
Add frontend service:
|
||||
```yaml
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "4200:80"
|
||||
depends_on:
|
||||
- api
|
||||
```
|
||||
|
||||
### Phase 3 verification
|
||||
|
||||
| Check | How |
|
||||
|-------|-----|
|
||||
| Full stack up | `docker compose up --build` -- 3 containers, all healthy |
|
||||
| Frontend accessible | Browse `http://localhost:4200` -- all pages load |
|
||||
| All data pages dynamic | schedule, donate, events all render from DB |
|
||||
| Data persists | `docker compose down && docker compose up` -- data still present |
|
||||
|
||||
## Files to Create (new)
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `.vscode/workspace.code-workspace` | Multi-root workspace (frontend + backend) |
|
||||
| `.vscode/settings.json` | Shared workspace settings |
|
||||
| `.vscode/tasks.json` | Common tasks (start backend, start frontend, docker up) |
|
||||
| `.vscode/launch.json` | Debug configs for both projects |
|
||||
| `backend/.vscode/settings.json` | Python-specific VS Code settings |
|
||||
| `backend/.vscode/launch.json` | FastAPI debug config |
|
||||
| `backend/requirements.txt` | Python dependencies |
|
||||
| `backend/Dockerfile` | API container image |
|
||||
| `backend/seed.py` | Initial station data |
|
||||
| `backend/app/main.py` | FastAPI app entry point |
|
||||
| `backend/app/config.py` | Env-based config |
|
||||
| `backend/app/database.py` | async session engine |
|
||||
| `backend/app/models.py` | SQLAlchemy ORM models |
|
||||
| `backend/app/schemas.py` | Pydantic schemas |
|
||||
| `backend/app/api/programs.py` | Programs CRUD endpoints |
|
||||
| `backend/app/api/events.py` | Events CRUD endpoints |
|
||||
| `backend/app/api/tiers.py` | Tiers CRUD endpoints |
|
||||
| `src/app/interfaces/program.ts` | Program TS type |
|
||||
| `src/app/interfaces/event.ts` | Event TS type |
|
||||
| `src/app/interfaces/tier.ts` | Tier TS type |
|
||||
| `src/app/services/program.service.ts` | Program HTTP service |
|
||||
| `src/app/services/event.service.ts` | Event HTTP service |
|
||||
| `src/app/services/tier.service.ts` | Tier HTTP service |
|
||||
| `src/environments/environment.ts` | Dev/prod API URL |
|
||||
| `docker-compose.yml` | 3-service compose |
|
||||
| `Dockerfile` | Angular build + nginx serve (at repo root) |
|
||||
| `nginx.conf` | nginx config with API proxy (at repo root) |
|
||||
|
||||
## Files to Modify
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `src/app/app.config.ts` | Add `provideHttpClient()` |
|
||||
| `src/app/schedule/schedule.component.ts` | Remove readonly array, inject service |
|
||||
| `src/app/events/events.component.ts` | Same pattern |
|
||||
| `src/app/donate/donate.component.ts` | Same pattern |
|
||||
| `docker-compose.yml` | Created in Phase 1, extended in Phase 3 |
|
||||
|
||||
## Not In Scope (for now)
|
||||
|
||||
- Admin panel (mentioned as Phase 5 in analysis — will be a future addition)
|
||||
- Contact form implementation (left as alert per user decision)
|
||||
- Streaming status endpoint (can add later via same pattern)
|
||||
Reference in New Issue
Block a user