Replaces a static SPA that shipped an Airtable PAT in the JS bundle.
The new architecture holds all secrets server-side, fronts the app
behind Apache on optical-dev with the shared-vhost split-build pattern,
and is designed for a later Azure AD/MSAL swap-in.
- backend/ FastAPI + uvicorn, local auth (Azure AD stub), Airtable
proxy with TTL cache, Zoho .xlsx/.csv parser, merge
service for utilisation summaries. 28 pytest tests.
- frontend/ React + Vite + TS + Tailwind + Recharts SPA. Login entry
chunk 12.83 KB gzipped; Recharts lazy-loaded. No tokens
or Airtable URLs in the built bundle.
- deploy/ Idempotent deploy.sh (port auto-pick 8200-8299,
.env-persisted) + split-build Apache include template.
- docker-compose.yml pins name: utilisation-dept and binds 127.0.0.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
30 lines
1.1 KiB
YAML
30 lines
1.1 KiB
YAML
# Pinned project name — required by global Docker policy so this stack
|
|
# can't collide with another /opt/<app>/docker-compose.yml that also
|
|
# defaults to the parent directory name.
|
|
name: utilisation-dept
|
|
|
|
services:
|
|
backend:
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile
|
|
image: utilisation-dept-backend:local
|
|
restart: unless-stopped
|
|
# Bind to localhost only — Apache fronts the public traffic.
|
|
ports:
|
|
- "127.0.0.1:${UTILISATION_DEPT_PORT:-8200}:8000"
|
|
# Secrets (especially the bcrypt hash with its $ characters) pass through
|
|
# verbatim via env_file, bypassing compose's ${...} interpolation.
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
- APP_BASE_PATH=/utilisation-dept
|
|
volumes:
|
|
# Persist the auth log across container rebuilds — replaces DB audit trail.
|
|
- ./backend/logs:/app/logs
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/api/health', timeout=2).status==200 else 1)"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|