diff --git a/CLAUDE.md b/CLAUDE.md index 1060716..fcc7fb1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,7 +30,7 @@ npm run lint # eslint ### Docker ```powershell -.\build-budget-image.ps1 # builds multi-stage image and pushes to docker.stwaddle.com/budget:latest +.\deploy.ps1 # builds image, pushes to docker.stwaddle.com/budget:latest, and deploys to prod via SSH ``` ## Architecture @@ -71,11 +71,18 @@ src/ - **Inline table editing**: rows show read-only data; clicking a cell enters edit mode in-place using a `useState(editing)` toggle. New rows use a separate input row appended to `` (not a modal). - **Sorting**: `SortableHeader` component and `SortState` type in `src/components/SortableHeader.tsx`. Sorting is client-side only and derives a view from the drag-ordered `displayItems` array — clearing sort restores drag order. Drag handles are disabled while a sort is active. - **Frequency calculations** for real-time previews are in `src/utils/frequency.ts`, mirroring the C# `FrequencyCalculator` exactly. +- **Drag-and-drop ordering**: `@dnd-kit` is used on income/outgo rows. Row order is persisted to the server. Sort handles are hidden while a column sort is active. - **Auth**: `react-oidc-context` wraps the whole app; `AuthGuard` protects all budget routes; `TokenSync` wires the OIDC access token into the API client on every auth state change. After OIDC callback, navigation uses `window.location.replace('/')` (not `history.replaceState`) so React Router picks up the URL change. +### Local development environment +Required env vars when running the API locally (set in `appsettings.Development.json` or shell): +- `ConnectionStrings__DefaultConnection` — Postgres connection string +- `Oidc__Audience` — `budget_api` +- `OTEL_EXPORTER_OTLP_ENDPOINT` — optional; omit or set to skip telemetry export + ### Auth / OIDC - Authority: `https://auth.stwaddle.com/` (OpenIddict-based, at `src.Auth.Server` in a sibling repo) -- API audience claim: `budget-api` (must match `AUTH__AUDIENCE` env var) +- API audience claim: `budget_api` (underscore — must match `Oidc__Audience` env var) - Scope: `budget_api` (underscore, not hyphen) - Roles `admin` and `user` are granted per-user on the client registration in the auth server admin panel. A user with no role gets `access_denied` at the authorize endpoint. - Frontend OIDC config is baked into the build via `VITE_*` env vars; override locally in `src/Budget.Client/.env.local` diff --git a/build-image.ps1 b/build-image.ps1 deleted file mode 100644 index c7cd532..0000000 --- a/build-image.ps1 +++ /dev/null @@ -1,4 +0,0 @@ -$image = "docker.stwaddle.com/budget:latest" -docker build -t $image . -if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } -docker push $image diff --git a/deploy.ps1 b/deploy.ps1 index 388ef54..ab421fc 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -1,2 +1,6 @@ -.\build-image.ps1 -.\update-container.ps1 \ No newline at end of file +$image = "docker.stwaddle.com/budget:latest" +docker build -t $image . +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +docker push $image +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +ssh stwaddle_com "cd stwaddlecom; docker compose pull budget; docker compose down budget; docker compose up -d budget" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index ae22497..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,46 +0,0 @@ -services: - budget: - image: ${IMAGE_REGISTRY:-}budget:latest - environment: - - VIRTUAL_HOST=${BUDGET_VIRTUAL_HOST:-budget.stwaddle.com} - - LETSENCRYPT_HOST=${BUDGET_LETSENCRYPT_HOST:-budget.stwaddle.com} - - VIRTUAL_PORT=8080 - - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Production} - - ConnectionStrings__DefaultConnection=Host=apps-db;Port=5432;Database=${POSTGRES_DB:-budget};Username=${POSTGRES_USER:-budget};Password=${POSTGRES_PASSWORD} - - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT} - - OTEL_EXPORTER_OTLP_PROTOCOL=${OTEL_EXPORTER_OTLP_PROTOCOL} - - OTEL_SERVICE_NAME=budget - depends_on: - - apps-db - - auth - networks: - - web - - apps-internal - - auth-public - - telemetry - restart: unless-stopped - - apps-db: - image: postgres:16-alpine - environment: - - POSTGRES_DB=${POSTGRES_DB:-budget} - - POSTGRES_USER=${POSTGRES_USER:-budget} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - volumes: - - apps-db-data:/var/lib/postgresql/data - networks: - - apps-internal - restart: unless-stopped - -networks: - web: - external: true - apps-internal: - external: true - auth-public: - external: true - telemetry: - external: true - -volumes: - apps-db-data: diff --git a/update-container.ps1 b/update-container.ps1 deleted file mode 100644 index c60d2e4..0000000 --- a/update-container.ps1 +++ /dev/null @@ -1 +0,0 @@ -ssh stwaddle_com "cd stwaddlecom; docker compose pull budget; docker compose down budget; docker compose up -d budget" \ No newline at end of file