working test deploy config
This commit is contained in:
@@ -67,7 +67,13 @@ Open [http://localhost:4200](http://localhost:4200) and ensure the backend is ru
|
||||
|
||||
```
|
||||
.
|
||||
├── docker-compose.yml # Dev compose (builds from source)
|
||||
├── docker-compose.prod.yml # Prod override (applied with -f docker-compose.yml -f docker-compose.prod.yml)
|
||||
├── docker-compose.dev.yml # Dev-sidecar compose (dev container)
|
||||
├── Dockerfile # Frontend: Node build stage + Nginx serve stage
|
||||
├── docker-entrypoint.sh # Renders nginx config + config.json from env vars
|
||||
├── nginx.conf.template # Nginx config template (envsubst)
|
||||
├── config.json.template # Runtime API config template (envsubst)
|
||||
├── .devcontainer/ # VS Code dev container
|
||||
│ ├── Dockerfile # Alpine Python 3.12 + Node.js + Angular CLI
|
||||
│ └── devcontainer.json # Dev container config
|
||||
@@ -75,8 +81,9 @@ Open [http://localhost:4200](http://localhost:4200) and ensure the backend is ru
|
||||
├── src/ # Angular 21 frontend
|
||||
│ ├── app/ # Standalone components
|
||||
│ │ ├── app.routes.ts # Lazy-loaded route map
|
||||
│ │ ├── app.config.ts # App providers + APP_INITIALIZER (loads config.json)
|
||||
│ │ ├── hero/ about/ schedule/ donate/ events/ contact/ navbar/ footer/
|
||||
│ │ ├── services/ # HTTP services (program, event, tier)
|
||||
│ │ ├── services/ # HTTP services (program, event, tier, app-config)
|
||||
│ │ └── interfaces/ # TypeScript types (Program, Event, Tier)
|
||||
│ ├── environments/ # dev/prod config (apiBaseUrl)
|
||||
│ └── styles/ # SCSS design tokens
|
||||
@@ -85,6 +92,7 @@ Open [http://localhost:4200](http://localhost:4200) and ensure the backend is ru
|
||||
│ └── _themes.scss # SCSS → CSS custom properties
|
||||
│
|
||||
└── backend/ # FastAPI backend
|
||||
├── Dockerfile # Python 3.12-slim + gunicorn/uvicorn
|
||||
├── requirements.txt
|
||||
├── seed.py # Initial data for all models
|
||||
└── app/
|
||||
@@ -130,12 +138,15 @@ curl -X POST http://localhost:8000/api/admin/reset
|
||||
|
||||
## Configuration
|
||||
|
||||
| Variable | Default | Where | Description |
|
||||
|---|---|---|---|
|
||||
| Variable | Default | Where | Description |
|
||||
|---|---|---|---|
|
||||
| `KMTN_DATABASE_URL` | `postgresql+asyncpg://...` (prod) / `sqlite+aiosqlite:///./kmountain.db` (dev) | [backend/app/config.py](backend/app/config.py) | Database connection string |
|
||||
| `KMTN_CORS_ORIGINS` | `["http://localhost:4200"]` | [backend/app/config.py](backend/app/config.py) | CORS allowlist (JSON array) |
|
||||
| `KMTN_ENV` | `development` | [backend/app/config.py](backend/app/config.py) | Set to `production` to disable admin endpoints |
|
||||
| `apiBaseUrl` | `http://localhost:8000` (dev) / `''` (prod) | [src/environments/](src/environments/) | Where the Angular app finds the API |
|
||||
| `API_UPSTREAM` | `http://api:8000` | [Dockerfile](Dockerfile) | Internal API target for Nginx proxy (Docker DNS) |
|
||||
| `API_PUBLIC_URL` | `""` (empty) | [Dockerfile](Dockerfile) | Browser-facing API URL. Empty = proxy mode; set to direct URL when ingress breaks proxy chain |
|
||||
|
||||
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.
|
||||
|
||||
@@ -182,7 +193,130 @@ Full interactive API docs at [http://localhost:8000/docs](http://localhost:8000/
|
||||
|
||||
## Deployment
|
||||
|
||||
### Production
|
||||
### Docker Compose (recommended)
|
||||
|
||||
The app ships as two Docker images — a frontend (Angular + Nginx) and a backend (FastAPI + gunicorn). Build, tag, and push them to your registry, then deploy with Docker Compose.
|
||||
|
||||
#### Build and push
|
||||
|
||||
```bash
|
||||
# Frontend
|
||||
docker build -t <registry>/kmtnflower:latest .
|
||||
docker push <registry>/kmtnflower:latest
|
||||
|
||||
# Backend
|
||||
docker build -t <registry>/kmtnflower-api:latest ./backend
|
||||
docker push <registry>/kmtnflower-api:latest
|
||||
```
|
||||
|
||||
#### Deploy
|
||||
|
||||
On the target machine, create a Docker Compose file (see [docker-compose.test.yml](docker-compose.test.yml) for a reference) that pulls the images and sets the required environment variables:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
db:
|
||||
image: docker.io/library/postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: kmountain
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
api:
|
||||
image: <registry>/kmtnflower-api:latest
|
||||
environment:
|
||||
KMTN_DATABASE_URL: postgresql+asyncpg://postgres:postgres@db:5432/kmountain
|
||||
KMTN_CORS_ORIGINS: '["http://yourdomain.com"]'
|
||||
KMTN_ENV: production
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
frontend:
|
||||
image: <registry>/kmtnflower:latest
|
||||
environment:
|
||||
API_UPSTREAM: http://api:8000
|
||||
API_PUBLIC_URL: ""
|
||||
ports:
|
||||
- "4200:80"
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
```
|
||||
|
||||
Then start:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
#### API\_PUBLIC\_URL
|
||||
|
||||
The frontend container renders a `config.json` at startup from the `API_PUBLIC_URL` environment variable. This controls how the Angular app reaches the API:
|
||||
|
||||
| Mode | Value | When to use |
|
||||
|---|---|---|
|
||||
| **Proxy** (default) | `""` (empty) | Browser calls `/api/*` relative paths; Nginx forwards to `API_UPSTREAM`. Use when no external ingress interferes. |
|
||||
| **Direct** | `"http://yourhost:8000"` | Browser calls the API container directly. Use when an external ingress layer (e.g., TrueNAS middleware, Kubernetes ingress) breaks the Nginx proxy chain. Requires CORS to be configured on the backend. |
|
||||
|
||||
#### Example: TrueNAS Scale
|
||||
|
||||
For a TrueNAS Scale deployment where the built-in ingress layer may redirect traffic:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
db:
|
||||
image: docker.io/library/postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: kmountain
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
api:
|
||||
image: truenas.local:35000/kmtnflower-api:latest
|
||||
environment:
|
||||
KMTN_DATABASE_URL: postgresql+asyncpg://postgres:postgres@db:5432/kmountain
|
||||
KMTN_CORS_ORIGINS: '["http://truenas.local:34200", "http://truenas.local"]'
|
||||
KMTN_ENV: production
|
||||
ports:
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
frontend:
|
||||
image: truenas.local:35000/kmtnflower:latest
|
||||
environment:
|
||||
API_UPSTREAM: http://api:8000
|
||||
API_PUBLIC_URL: "http://truenas.local:8000"
|
||||
ports:
|
||||
- "34200:80"
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
```
|
||||
|
||||
> **Note:** The database auto-seeds with initial data on first startup. To re-seed at any time (dev only): `curl -X POST http://localhost:8000/api/admin/reset`.
|
||||
|
||||
### Bare-metal production
|
||||
|
||||
For production, you'll need to set up:
|
||||
- A PostgreSQL database (managed or self-hosted)
|
||||
|
||||
Reference in New Issue
Block a user