Commit graph

3 commits

Author SHA1 Message Date
DJP
88e4df59be Validate persisted ports against live host on every deploy
Previously the script only ran port resolution when no compose
containers were already running, on the assumption that running
containers meant the persisted port was still ours. On the shared
optical-dev server that's wrong: another app can grab a port
between our deploys, leaving us with a stale .env value that fails
to bind.

Now find_free_port treats a port as "available for us" if either
nothing is listening, or one of our own compose containers is
publishing it (so re-deploys don't shuffle). Other-app listeners
trigger a re-pick and a warning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:22:06 -04:00
DJP
8e969fe015 Add login + role-based access (admin/user) backed by Postgres
Adds a Postgres-backed user store with bcrypt + JWT cookie sessions,
login screen, role-gated UI, and Microsoft SSO scaffolding ready to
fill in.

Backend
- New `db` service (Postgres 16-alpine) in compose, healthcheck-gated
  app startup, free-port autodetect (5435-5499) like other apps.
- `server/db.js` runs versioned `.sql` migrations on boot.
- `server/auth.js`: bcrypt + JWT cookie (httpOnly, sameSite=strict,
  path-scoped to /adeo-maturity), rate-limited login (10/15min),
  dummy bcrypt-compare on missing users to defeat timing oracles.
- `requireAdmin` on all writes (POST/import/sync); `authenticate`
  on all reads. /api/health stays public.
- Microsoft SSO endpoints stubbed at /api/auth/msft/{login,callback}
  (return 501); DB has azure_oid column ready; comments document
  exactly how to wire @azure/msal-node.

Frontend
- Login screen with email/password + greyed-out "Sign in with
  Microsoft" button; init() checks /api/auth/me first.
- Logout button + user badge in header.
- body.role-user CSS hides .admin-only elements (Update tab, New
  Client cards). Server enforces regardless.

Deploy
- deploy.sh generates DB_PASSWORD and AUTH_SECRET on first run and
  persists to .env, then runs `seed-users.js seed-defaults` to
  create admin@oliver.agency + user@oliver.agency with random
  passwords printed once. Subsequent deploys skip seeding unless
  --reseed is passed.
- node server/seed-users.js set-password <email> <pw> for ad-hoc
  resets later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:14:19 -04:00
DJP
6dbd399ac0 Restructure deploy to mirror oliver-sales-ops-platform pattern
Subpath-aware deploy for /adeo-maturity/ behind the shared optical-dev
vhost. Auto-picks a free host port (prefers 3102, scans 3102-3199) and
persists it to .env so re-deploys are idempotent. Renders the Apache
conf from a template on each run.

- script.js: detect URL prefix at load time and prepend it to all /api/
  calls, so the same code works at the root locally and under a
  sub-path behind Apache.
- Dockerfile: fix broken package.json copy (lives at repo root, not
  server/) and install python3 + reportlab + openpyxl for the sync/
  import/PDF endpoints that shell out.
- docker-compose: pin top-level name (per global docker policy),
  configurable host port, bind 127.0.0.1 only.
- deploy/: new deploy.sh + apache-adeo.conf.tmpl. Old root deploy.sh
  removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 13:33:49 -04:00