Add README and fix Dockerfile VITE build args
README covers all env vars, docker-compose/env examples, and full auth server setup (scope, client registration, user roles). Dockerfile now accepts VITE_AUTH_* build args with production defaults so the values are baked into the client bundle correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,15 @@ WORKDIR /app/client
|
|||||||
COPY src/Budget.Client/package*.json ./
|
COPY src/Budget.Client/package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
COPY src/Budget.Client/ ./
|
COPY src/Budget.Client/ ./
|
||||||
|
|
||||||
|
ARG VITE_AUTH_AUTHORITY=https://auth.stwaddle.com/
|
||||||
|
ARG VITE_AUTH_CLIENT_ID=budget-client
|
||||||
|
ARG VITE_AUTH_REDIRECT_URI=https://budget.stwaddle.com/callback
|
||||||
|
|
||||||
|
ENV VITE_AUTH_AUTHORITY=$VITE_AUTH_AUTHORITY
|
||||||
|
ENV VITE_AUTH_CLIENT_ID=$VITE_AUTH_CLIENT_ID
|
||||||
|
ENV VITE_AUTH_REDIRECT_URI=$VITE_AUTH_REDIRECT_URI
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Stage 2: Build and publish ASP.NET app
|
# Stage 2: Build and publish ASP.NET app
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
# Budget
|
||||||
|
|
||||||
|
A multi-user budget web app built around the 50/30/20 framework. Users manage Income and Outgo entries, view a Summary breakdown by type (Need / Want / Save), and can share budgets with other users in edit or view-only mode.
|
||||||
|
|
||||||
|
**Stack:** ASP.NET Core 10 + Vite/React (TypeScript) — served as a single Docker container. PostgreSQL via EF Core. OIDC authentication against `auth.stwaddle.com`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Backend (ASP.NET Core)
|
||||||
|
|
||||||
|
| Variable | Required | Description |
|
||||||
|
|----------|----------|-------------|
|
||||||
|
| `ConnectionStrings__DefaultConnection` | Yes | Full Npgsql connection string |
|
||||||
|
| `AUTH__AUTHORITY` | Yes | OIDC authority base URL (trailing slash required) |
|
||||||
|
| `AUTH__AUDIENCE` | Yes | Expected `aud` claim in access tokens — must match the scope's Resource value in the auth server |
|
||||||
|
| `ASPNETCORE_ENVIRONMENT` | No | `Production` or `Development` (default: `Production` in Docker) |
|
||||||
|
|
||||||
|
If `ConnectionStrings__DefaultConnection` is absent, the app falls back to assembling a connection string from individual variables:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `POSTGRES_HOST` | `localhost` | Database host |
|
||||||
|
| `POSTGRES_PORT` | `5432` | Database port |
|
||||||
|
| `POSTGRES_DB` | `budget` | Database name |
|
||||||
|
| `POSTGRES_USER` | `budget` | Database user |
|
||||||
|
| `POSTGRES_PASSWORD` | `changeme` | Database password |
|
||||||
|
|
||||||
|
### Frontend (Vite — baked in at build time)
|
||||||
|
|
||||||
|
These are compiled into the static assets during the Docker build and cannot be changed at runtime.
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `VITE_AUTH_AUTHORITY` | OIDC authority base URL — must match `AUTH__AUTHORITY` |
|
||||||
|
| `VITE_AUTH_CLIENT_ID` | OIDC client ID registered in the auth server |
|
||||||
|
| `VITE_AUTH_REDIRECT_URI` | Full URL of the `/callback` route (e.g. `https://budget.stwaddle.com/callback`) |
|
||||||
|
|
||||||
|
To change frontend auth config after the image is built, rebuild the image with updated build args. See the Dockerfile.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
The service is defined in `docker/docker-compose.yaml` and managed alongside the rest of the stwaddle.com infrastructure.
|
||||||
|
|
||||||
|
### docker-compose service
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
budget:
|
||||||
|
image: docker.stwaddle.com/budget:latest
|
||||||
|
environment:
|
||||||
|
- VIRTUAL_HOST=budget.stwaddle.com
|
||||||
|
- LETSENCRYPT_HOST=budget.stwaddle.com
|
||||||
|
- VIRTUAL_PORT=8080
|
||||||
|
- ASPNETCORE_ENVIRONMENT=Production
|
||||||
|
- ConnectionStrings__DefaultConnection=Host=apps-db;Port=5432;Database=budget;Username=postgres;Password=${APPS_DB_PASSWORD}
|
||||||
|
- AUTH__AUTHORITY=https://auth.stwaddle.com/
|
||||||
|
- AUTH__AUDIENCE=budget-api
|
||||||
|
depends_on:
|
||||||
|
- apps-db
|
||||||
|
- auth
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- apps-internal
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
### .env entries
|
||||||
|
|
||||||
|
Add to the `.env` file on the server (alongside the existing entries):
|
||||||
|
|
||||||
|
```env
|
||||||
|
BUDGET_VIRTUAL_HOST=budget.stwaddle.com
|
||||||
|
BUDGET_LETSENCRYPT_HOST=budget.stwaddle.com
|
||||||
|
```
|
||||||
|
|
||||||
|
`APPS_DB_PASSWORD` is already set in `.env` for the recipes app and is shared. The budget app gets its own `budget` database within the same Postgres instance — EF Core's `MigrateAsync()` creates it automatically on first startup.
|
||||||
|
|
||||||
|
### Build and push
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build-budget-image.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This builds the multi-stage Docker image (Node → .NET SDK → ASP.NET runtime) and pushes it to `docker.stwaddle.com/budget:latest`.
|
||||||
|
|
||||||
|
The `VITE_*` build args default to the production values in the Dockerfile. To override:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker build `
|
||||||
|
--build-arg VITE_AUTH_AUTHORITY=https://auth.stwaddle.com/ `
|
||||||
|
--build-arg VITE_AUTH_CLIENT_ID=budget-client `
|
||||||
|
--build-arg VITE_AUTH_REDIRECT_URI=https://budget.stwaddle.com/callback `
|
||||||
|
-t docker.stwaddle.com/budget:latest .
|
||||||
|
docker push docker.stwaddle.com/budget:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
Add an A record for `budget.stwaddle.com` pointing to the Linode server IP before deploying. The acme-companion will issue the Let's Encrypt certificate automatically. If DNS hasn't propagated when the container first starts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose restart acme-companion
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auth Server Setup
|
||||||
|
|
||||||
|
The budget app requires two registrations in the auth server admin panel at `https://auth.stwaddle.com/Admin/` (requires an account with the Admin role).
|
||||||
|
|
||||||
|
### 1. Create the API Scope
|
||||||
|
|
||||||
|
> **Admin → Scopes → Create**
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Name | `budget_api` |
|
||||||
|
| Display Name | `Budget API` |
|
||||||
|
| Resources | `budget-api` |
|
||||||
|
|
||||||
|
The **Resources** field is what gets written into the `aud` claim of access tokens. It must exactly match `AUTH__AUDIENCE=budget-api` set on the API container.
|
||||||
|
|
||||||
|
### 2. Register the Client Application
|
||||||
|
|
||||||
|
> **Admin → Sites → Create**
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Client ID | `budget-client` |
|
||||||
|
| Display Name | `Budget` |
|
||||||
|
| Redirect URIs | `https://budget.stwaddle.com/callback` |
|
||||||
|
| Post-Logout Redirect URIs | `https://budget.stwaddle.com` |
|
||||||
|
| API Scopes | ✅ `budget_api` |
|
||||||
|
|
||||||
|
The client is always created as **Public** (no secret) with **Authorization Code + PKCE + Refresh Token** — this is enforced by the auth server for all SPA clients.
|
||||||
|
|
||||||
|
For local development, also add the dev URIs (you can edit the client after creation):
|
||||||
|
- Redirect URI: `http://localhost:5173/callback`
|
||||||
|
- Post-Logout URI: `http://localhost:5173`
|
||||||
|
|
||||||
|
### 3. Grant Users Access
|
||||||
|
|
||||||
|
Users must be explicitly granted a role on the client before they can log in.
|
||||||
|
|
||||||
|
> **Admin → Sites → [Budget] → Roles → Grant**
|
||||||
|
|
||||||
|
Assign any user who needs access at least the `user` role. Without a role entry the auth server returns `access_denied` at the authorize endpoint.
|
||||||
|
|
||||||
|
### Token details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Flow | Authorization Code + PKCE |
|
||||||
|
| Access token lifetime | 15 minutes |
|
||||||
|
| Refresh token lifetime | 14 days |
|
||||||
|
| Claims in access token | `sub`, `email`, `name`, `role`, `aud` (`budget-api`) |
|
||||||
|
|
||||||
|
The frontend uses `oidc-client-ts` with automatic silent renewal, so users stay logged in across the 15-minute access token window without re-authenticating.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- .NET 10 SDK
|
||||||
|
- Node 22
|
||||||
|
- PostgreSQL (or Docker)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Copy `.env.example` to `.env` and fill in values for your local Postgres instance.
|
||||||
|
|
||||||
|
2. Start the API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd src/Budget.Api
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
The API listens on `http://localhost:5000` by default. EF migrations run automatically on startup.
|
||||||
|
|
||||||
|
3. Start the frontend dev server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd src/Budget.Client
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Vite proxies `/api` requests to `http://localhost:5000`. The app is available at `http://localhost:5173`.
|
||||||
|
|
||||||
|
4. Set the following in `src/Budget.Client/.env.local` (Vite picks this up automatically, it is gitignored):
|
||||||
|
|
||||||
|
```env
|
||||||
|
VITE_AUTH_AUTHORITY=https://auth.stwaddle.com/
|
||||||
|
VITE_AUTH_CLIENT_ID=budget-client
|
||||||
|
VITE_AUTH_REDIRECT_URI=http://localhost:5173/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Set the following in `src/Budget.Api/appsettings.Development.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"AUTH__AUTHORITY": "https://auth.stwaddle.com/",
|
||||||
|
"AUTH__AUDIENCE": "budget-api"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The auth server's dev redirect URI (`http://localhost:5173/callback`) must be registered on the client as described in the Auth Server Setup section above.
|
||||||
Reference in New Issue
Block a user