diff --git a/.env.example b/.env.example index c91ed1f..22042ac 100644 --- a/.env.example +++ b/.env.example @@ -16,12 +16,16 @@ ADMIN_PASSWORD=your-secure-password ANTHROPIC_API_KEY=sk-ant-... # OPENAI_API_KEY=sk-... -# Auth (required for login/session; NextAuth) +# Auth (required for login/session; NextAuth / Auth.js) AUTH_SECRET=generate-with-openssl-rand-base64-32 # Dev only: set this to log in as ANY email with this password (skips DB). Use when DB login fails. # AUTH_DEV_PASSWORD=changeme -# Production: must match your public URL (stops redirect loops) +# Production: origin only (https://evenslouis.ca). Do NOT append /pro — Auth.js mis-parses +# /api/auth/* actions (UnknownAction) if AUTH_URL/NEXTAUTH_URL includes a path. NEXTAUTH_URL=https://evenslouis.ca +# Required for Compose `pro` service (NEXT_PUBLIC_BASE_PATH=/pro on :3204) +AUTH_TRUST_HOST=true +# NEXT_PUBLIC_BASE_PATH is set by docker-compose for the `pro` service only — do not set on root app. # OAuth (prod): add for real Google sign-in. Get from https://console.cloud.google.com/apis/credentials # GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com @@ -39,7 +43,7 @@ NEXTAUTH_URL=https://evenslouis.ca PIPELINE_DRY_RUN=0 # E2E / smoke tests: use 1 to run pipeline without LLM (placeholder artifacts) # PIPELINE_DRY_RUN=1 -# Prod e2e (learning ingest, etc.): set so Playwright can log in at PLAYWRIGHT_BASE_URL=https://evenslouis.ca +# Prod e2e (operator OS): PLAYWRIGHT_BASE_URL=https://evenslouis.ca/pro # E2E_EMAIL=your-prod-login@example.com # E2E_PASSWORD=your-prod-password # On the production server, also set these so e2e login works without relying on DB password: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1713e26..942b89b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ### Added - Path-aware `/pro` production build: environment-driven Next.js base path, prefixed browser API/navigation helpers, and an isolated Docker Compose service on `127.0.0.1:3204` while preserving the root `:3200` app. +- Operator/docs cutover for canonical home `https://evenslouis.ca/pro`: smoke-test covers public + `/pro`, deploy checklists and README/ROADMAP point operator paths at `/pro`, Auth.js origin-only `NEXTAUTH_URL` + `AUTH_TRUST_HOST` documented. + +## [2026-08-07] — /pro on main + +### Added +- Merged isolated `/pro` basePath deployment onto `main` (PR #10 / ADR 007). +- Session notes: `docs/sessions/2026-08-06-pro-domain-migration.md`, `docs/sessions/2026-08-07-pro-cutover-main.md`. ### Fixed - Conversion page crash "Cannot read properties of undefined (reading 'winRate')" — API now returns page-expected shape (counts.total/won/lost, rates.winRate, medianMs); fetchConversionInput adds won/lost from Lead.dealOutcome; route maps to counts/rates/medianMs; page useEffect uses defensive guards diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index d7ae47b..c14fa8d 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -13,6 +13,8 @@ ACCEPT → PROPOSE → BUILD. Nothing ships unless explicitly allowed. **Baseline:** main = deployable truth (auth, schema, API hardening, health, retry, dry-run, error classifier, E2E scaffold). +**Canonical operator home (2026-08):** `https://evenslouis.ca/pro/dashboard` via isolated Compose `pro` service (`NEXT_PUBLIC_BASE_PATH=/pro` on `:3204`). Public site stays at `https://evenslouis.ca` (`:3200`). See ADR 007. Do not start Architecture Phase 2–4 until `/pro` cut + smoke are closed. + --- ## 1. What ships now (client-engine-1 — “Money Path Machine”) diff --git a/README.md b/README.md index 4ac3a7b..59eac4b 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,12 @@ bash deploy.sh 1. **Env (VPS or `.env`):** Must include: - `DATABASE_URL` - `AUTH_SECRET` - - `NEXTAUTH_URL` (e.g. `https://evenslouis.ca`) - - `OPENAI_API_KEY` + - `NEXTAUTH_URL` (origin only, e.g. `https://evenslouis.ca` — **do not** append `/pro`) + - `AUTH_TRUST_HOST=true` (required for the `/pro` Compose service) + - `ANTHROPIC_API_KEY` and/or `OPENAI_API_KEY` 2. **Database:** `deploy.sh` runs `prisma db push`. If you deploy without it, run `npx prisma db push` on the server once. -3. **Health:** After deploy, confirm `GET /api/health` returns **200** with `ok: true` and all checks true (db, pipelineTables, authSecret, nextAuthUrl). +3. **Health:** After deploy, confirm root `GET /api/health` and operator `GET /pro/api/health` both return **200** with `ok: true`. +4. **Architecture:** Root app `:3200` (public) + isolated `pro` service `:3204` with `NEXT_PUBLIC_BASE_PATH=/pro`. See [ADR 007](docs/decisions/007-pro-base-path-deployment.md). ## Operations @@ -50,15 +52,18 @@ bash logs.sh postgres # Tail DB logs **One-command deploy from your machine:** Use `./scripts/sync-and-deploy.sh` to keep dev and prod in sync (push, rsync, deploy). If the server has an SSH deploy key, you can use `./scripts/deploy-remote.sh` instead. See [docs/DEPLOY_SSH_SETUP.md](docs/DEPLOY_SSH_SETUP.md). -**Post-deploy smoke test:** `./scripts/smoke-test.sh` (or `./scripts/smoke-test.sh https://evenslouis.ca`) — checks homepage, login, dashboard, `/api/health`, `/api/ops/command`, SSL. Exit 0 = all pass. +**Post-deploy smoke test:** `./scripts/smoke-test.sh` — checks public site + `/pro` operator health/login/dashboard. Exit 0 = all pass. **VPS out of disk (ENOSPC / rsync or deploy fails):** Run `./scripts/run-vps-cleanup.sh` from your Mac to prune Docker and free space on the server, then run `./scripts/sync-and-deploy.sh` again. See [docs/VPS_DEPLOY_CHECKLIST.md](docs/VPS_DEPLOY_CHECKLIST.md) § Disk space maintenance. ## URLs -- **https://evenslouis.ca** — Public site -- **https://evenslouis.ca/dashboard** — Private dashboard (login required) -- **https://evenslouis.pro** — Redirects to dashboard +- **https://evenslouis.ca** — Public site (root app `:3200`) +- **https://evenslouis.ca/pro/dashboard** — Operator OS (isolated `pro` app `:3204`, `basePath=/pro`) +- **https://evenslouis.ca/pro/login** — Operator login +- **https://evenslouis.pro** — Legacy host; redirect to `/pro` only after operator smoke is green (see ADR 007) + +Public marketing/proof/campaign/portal pages stay on the root host unless product intent moves them. ## Local development @@ -99,7 +104,7 @@ To run without an OpenAI key (pipeline uses placeholder artifacts): PIPELINE_DRY_RUN=1 npm run test:e2e ``` -**Production:** Use `PLAYWRIGHT_BASE_URL=https://evenslouis.ca` only after fixing redirect loops (set `NEXTAUTH_URL` and `AUTH_SECRET` on the server). +**Production operator E2E:** `PLAYWRIGHT_BASE_URL=https://evenslouis.ca/pro` (set `NEXTAUTH_URL` to the origin without `/pro`, plus `AUTH_SECRET` / `AUTH_TRUST_HOST`). **Testing strategy:** [docs/TESTING_SIDE_PANEL.md](docs/TESTING_SIDE_PANEL.md) — two-tier approach (Playwright automated + manual production checks), page-by-page test matrix, and embedded browser notes. diff --git a/ROADMAP.md b/ROADMAP.md index 670baa1..dae3dcf 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,12 +1,21 @@ # Roadmap — Client Engine -## Current State (March 2026) +## Current State (August 2026) Phase 9+ complete. Full business OS operational with AI Brain, 10 agents, memory pipeline, NBA system, risk engine, scoring, notifications, growth engine, signal engine, builder integration, Outcome Ledger + Scorecard (Sprint 9), and 92 dashboard pages. +**Canonical operator home:** `https://evenslouis.ca/pro/dashboard` (isolated Compose `pro` service on `:3204`, ADR 007). Public site remains `https://evenslouis.ca` (`:3200`). + ## Active Work -### Performance Refactor (Phase 1 — Done, pending production deploy) +### `/pro` cutover (current) +- [x] Isolated `/pro` basePath deploy on main (PR #10 / ADR 007) +- [x] VPS: `pro` healthy on `127.0.0.1:3204`; Caddy `/pro*`; login → `/pro/dashboard`; `/pro/api/health` +- [ ] Finish Sprint 1–9 checklist smoke at `/pro/...` URLs ([DEPLOY_CHECKLIST_SPRINTS_1_9.md](docs/DEPLOY_CHECKLIST_SPRINTS_1_9.md) §5) +- [ ] Redirect `evenslouis.pro` → `https://evenslouis.ca/pro` after smoke +- [ ] Keep docs/smoke/E2E pointed at `/pro` for operator paths + +### Performance Refactor (Phase 1 — Done) - [x] Add 6 composite database indexes - [x] Fix unbounded queries in metrics - [x] Parallelize sequential queries (3 files) @@ -15,7 +24,7 @@ Phase 9+ complete. Full business OS operational with AI Brain, 10 agents, memory - [x] Add cache to fetchBottlenecks - [x] Fix LIKE pattern full scan - [x] Playwright review: all pages pass, zero console errors -- [ ] Apply indexes to production (`prisma db push` on VPS) +- [x] Apply indexes to production (`db4c0de`) ### Documentation System — Done - [x] CLAUDE.md, ARCHITECTURE.md, CONTRIBUTING.md, CHANGELOG.md, ROADMAP.md diff --git a/docs/AFTER_DEPLOY_SMOKE_CHECKLIST.md b/docs/AFTER_DEPLOY_SMOKE_CHECKLIST.md index 0b45375..7578d97 100644 --- a/docs/AFTER_DEPLOY_SMOKE_CHECKLIST.md +++ b/docs/AFTER_DEPLOY_SMOKE_CHECKLIST.md @@ -3,6 +3,9 @@ Run immediately after every production deploy. Do not skip. **Goal:** confirm nothing broke. If anything fails, rollback first, investigate second. +**Canonical operator home:** `https://evenslouis.ca/pro/dashboard` +**Public site:** `https://evenslouis.ca` + --- ## Automated (30 seconds) @@ -10,14 +13,15 @@ Run immediately after every production deploy. Do not skip. Run these from your terminal. Both must pass before you do manual checks. ```bash -# 1. Curl-based smoke test (homepage, login, dashboard, health, ops/command, SSL) -./scripts/smoke-test.sh https://evenslouis.ca +# 1. Curl-based smoke test (public + /pro login/dashboard/health) +./scripts/smoke-test.sh https://evenslouis.ca https://evenslouis.ca/pro # 2. Health only (quick sanity) curl -s https://evenslouis.ca/api/health +curl -s https://evenslouis.ca/pro/api/health ``` -**Expected:** smoke-test.sh exits 0. Health returns `{ "ok": true }` with all checks green. +**Expected:** smoke-test.sh exits 0. Both health endpoints return `{ "ok": true }` with checks green. **If health fails → rollback immediately:** ```bash @@ -28,16 +32,16 @@ ssh $DEPLOY_SERVER 'cd /root/client-engine && git reset --hard HEAD~1 && bash de ## Manual checks (3–5 min) -Open production in MCP browser or a real browser. +Open production in a real browser (operator paths under `/pro`). | # | Check | How | Pass | Fail → do this | |---|-------|-----|------|-----------------| -| 1 | **Login** | `https://evenslouis.ca/login` → log in | Dashboard loads, no redirect loop | Rollback. Check `NEXTAUTH_URL`, `AUTH_SECRET`, run `npm run reset-auth` on VPS | -| 2 | **Command Center** | Open `/dashboard/command` | Scorecard renders, Failures card renders, data is not stale | Rollback. Check server logs for query errors | -| 3 | **Lead detail** | Open any lead at `/dashboard/leads/[id]` | Artifacts load, pipeline actions visible | Rollback. Check DB connectivity | -| 4 | **Proposals** | Open `/dashboard/proposals` | List loads, at least one proposal visible (if any exist) | Rollback | -| 5 | **Metrics** | Open `/dashboard/metrics` | Page loads, recent runs visible | Rollback | -| 6 | **API auth gate** | `curl -s https://evenslouis.ca/api/leads` | Returns 401 (no cookie = auth working) | **Critical:** Auth is broken. Rollback immediately | +| 1 | **Login** | `https://evenslouis.ca/pro/login` → log in | `/pro/dashboard` loads, no redirect loop | Rollback. Check `NEXTAUTH_URL` is origin-only (no `/pro`), `AUTH_TRUST_HOST=true`, `AUTH_SECRET`, run `npm run reset-auth` on VPS | +| 2 | **Command Center** | Open `/pro/dashboard/command` (or founder home) | Scorecard/Failures render, data is not stale | Rollback. Check server logs for query errors | +| 3 | **Lead detail** | Open any lead at `/pro/dashboard/leads/[id]` | Artifacts load, pipeline actions visible | Rollback. Check DB connectivity | +| 4 | **Proposals** | Open `/pro/dashboard/proposals` | List loads | Rollback | +| 5 | **Metrics** | Open `/pro/dashboard/metrics` | Page loads | Rollback | +| 6 | **API auth gate** | `curl -s https://evenslouis.ca/pro/api/leads` | Returns 401 (no cookie = auth working) | **Critical:** Auth is broken. Rollback immediately | | 7 | **SSL** | smoke-test.sh checks this, or manually check cert | Certificate valid, not expired | Renew cert (certbot or hosting panel) | --- @@ -51,21 +55,24 @@ If any check fails after deploy: ssh $DEPLOY_SERVER '/root/rollback-client-engine.sh' # Option B: manual -ssh $DEPLOY_SERVER 'cd /root/client-engine && git log --oneline -5 && git reset --hard HEAD~1 && bash deploy.sh && curl -fsS https://evenslouis.ca/api/health' +ssh $DEPLOY_SERVER 'cd /root/client-engine && git log --oneline -5 && git reset --hard HEAD~1 && bash deploy.sh && curl -fsS https://evenslouis.ca/pro/api/health' ``` See [DEPLOY_SSH_SETUP.md](DEPLOY_SSH_SETUP.md) for full rollback details. --- -## Optional: run Playwright against prod +## Optional: run Playwright against prod operator OS ```bash -USE_EXISTING_SERVER=1 PLAYWRIGHT_BASE_URL=https://evenslouis.ca npm run test:e2e +USE_EXISTING_SERVER=1 PLAYWRIGHT_BASE_URL=https://evenslouis.ca/pro npm run test:e2e ``` -21 tests run without login. Set `E2E_EMAIL`/`E2E_PASSWORD` for all tests. +Set `E2E_EMAIL`/`E2E_PASSWORD` for authenticated tests. Keep `NEXTAUTH_URL` as origin-only. --- -*See also: [VPS_DEPLOY_CHECKLIST.md](VPS_DEPLOY_CHECKLIST.md) (full deploy process), [TESTING_SIDE_PANEL.md](TESTING_SIDE_PANEL.md) (testing strategy).* +## Related + +- Full Sprint 1–9 path smoke: [DEPLOY_CHECKLIST_SPRINTS_1_9.md](DEPLOY_CHECKLIST_SPRINTS_1_9.md) §5 (use `/pro/...` URLs) +- Architecture: [decisions/007-pro-base-path-deployment.md](decisions/007-pro-base-path-deployment.md) diff --git a/docs/BEFORE_CLIENTS_CHECKLIST.md b/docs/BEFORE_CLIENTS_CHECKLIST.md index 2f12b94..e236704 100644 --- a/docs/BEFORE_CLIENTS_CHECKLIST.md +++ b/docs/BEFORE_CLIENTS_CHECKLIST.md @@ -9,8 +9,8 @@ Run before any client call, demo, screenshare, or proposal send. | # | Check | How | Pass | Fail → do this | |---|-------|-----|------|-----------------| -| 1 | **Health** | `curl -s https://evenslouis.ca/api/health` | 200, `ok: true`, all checks green | Do not demo. Fix health first (see [VPS_DEPLOY_CHECKLIST.md](VPS_DEPLOY_CHECKLIST.md)) | -| 2 | **Command Center** | Open `/dashboard/command` | Scorecard, Failures, Constraint all render with current data | If cards are blank → check last workday run, restart app | +| 1 | **Health** | `curl -s https://evenslouis.ca/pro/api/health` (and root `/api/health`) | 200, `ok: true`, all checks green | Do not demo. Fix health first (see [VPS_DEPLOY_CHECKLIST.md](VPS_DEPLOY_CHECKLIST.md)) | +| 2 | **Command Center** | Open `/pro/dashboard/command` (or founder home) | Scorecard, Failures, Constraint all render with current data | If cards are blank → check last workday run, restart app | | 3 | **Target lead** | Open the lead(s) you'll reference | Artifacts present, proposal content correct, no stale data | If missing → run pipeline or check for errors | | 4 | **Proposal console** | Open the relevant proposal at `/dashboard/proposals/[id]` | Sections render, snippet correct, ready/sent toggles work | If stale → revise proposal before the call | | 5 | **No visible errors** | Open browser DevTools Console on pages you'll show | No red errors | Fix or note before screensharing | diff --git a/docs/DEPLOY_CHECKLIST_SPRINTS_1_9.md b/docs/DEPLOY_CHECKLIST_SPRINTS_1_9.md index f1cb3e5..2b2772d 100644 --- a/docs/DEPLOY_CHECKLIST_SPRINTS_1_9.md +++ b/docs/DEPLOY_CHECKLIST_SPRINTS_1_9.md @@ -84,8 +84,10 @@ Deploy scripts run `prisma migrate deploy` automatically. If deploying manually: ## 5. Smoke Test Production (do these manually) +**Use operator URLs under `https://evenslouis.ca/pro/...`** (isolated `pro` service). Public proof/campaign/portal pages remain on the root host unless product intent moves them. + ### Critical path — must all work -- [ ] **Login** — dashboard loads, no blank screens +- [ ] **Login** — `https://evenslouis.ca/pro/login` → `/pro/dashboard` loads, no blank screens - [ ] **Sidebar** — shows all 6 groups: Capture, Convert, Build, Prove, Optimize, System - [ ] **Create a lead** — fill form, submit, verify it appears in pipeline - [ ] **Pipeline view** — leads visible with correct status badges diff --git a/docs/VPS_DEPLOY_CHECKLIST.md b/docs/VPS_DEPLOY_CHECKLIST.md index bb1029a..b87814d 100644 --- a/docs/VPS_DEPLOY_CHECKLIST.md +++ b/docs/VPS_DEPLOY_CHECKLIST.md @@ -8,7 +8,8 @@ |----------|----------|--------| | `DATABASE_URL` | Yes | PostgreSQL connection string | | `AUTH_SECRET` | Yes | NextAuth secret; generate with `openssl rand -base64 32` | -| `NEXTAUTH_URL` | Yes | Full app URL e.g. `https://evenslouis.ca` | +| `NEXTAUTH_URL` | Yes | Origin only e.g. `https://evenslouis.ca` (**no** `/pro` path) | +| `AUTH_TRUST_HOST` | Yes (for `/pro`) | `true` — required by Compose `pro` service | | `ADMIN_EMAIL` | For login | Used by seed/reset-auth to create the single admin user (default `admin@evenslouis.ca`) | | `ADMIN_PASSWORD` | For login | Used by seed/reset-auth; use the same value when you run reset-auth and when you log in (default `changeme`) | | `OPENAI_API_KEY` | For pipeline | Omit or use dry-run for no LLM calls | @@ -107,23 +108,25 @@ Run immediately after every production deploy. Do not skip. ### Automated (fast, 30 seconds) ```bash -# Curl-based smoke test -./scripts/smoke-test.sh https://evenslouis.ca +# Curl-based smoke test (public site + /pro operator) +./scripts/smoke-test.sh https://evenslouis.ca https://evenslouis.ca/pro # Health only curl -s https://evenslouis.ca/api/health +curl -s https://evenslouis.ca/pro/api/health ``` Both must pass. If health check fails, **rollback immediately** (see below). ### Manual production checks (MCP browser or real browser, 3-5 min) -- [ ] **Login:** Open `https://evenslouis.ca/login`, log in → dashboard loads +- [ ] **Operator login:** Open `https://evenslouis.ca/pro/login`, log in → `/pro/dashboard` loads +- [ ] **Pro health:** `curl -s https://evenslouis.ca/pro/api/health` → 200 - [ ] **Command Center:** Scorecard renders, Failures card renders, data is current - [ ] **One lead detail:** Open any lead → artifacts load, pipeline actions visible - [ ] **Proposals:** List loads, at least one proposal visible (if any exist) - [ ] **Metrics:** Page loads, recent runs visible -- [ ] **API auth gate:** `curl -s https://evenslouis.ca/api/leads` → 401 (confirms auth is working) +- [ ] **API auth gate:** `curl -s https://evenslouis.ca/pro/api/leads` → 401 (confirms auth is working) - [ ] **SSL:** Certificate valid (smoke-test.sh checks this for HTTPS) If any check fails after deploy, rollback first, investigate second. See `docs/DEPLOY_SSH_SETUP.md` for rollback commands. diff --git a/docs/sessions/2026-08-07-pro-cutover-main.md b/docs/sessions/2026-08-07-pro-cutover-main.md new file mode 100644 index 0000000..3a1bafc --- /dev/null +++ b/docs/sessions/2026-08-07-pro-cutover-main.md @@ -0,0 +1,33 @@ +# Session: /pro cutover onto main + +## Goal + +Land the isolated `/pro` operator OS on `main`, verify live VPS wiring, and sweep docs/smoke so the next deploy does not regress to root `/dashboard` URLs. + +## Decisions + +- Merge PR #10 (`cursor/domain-path-consolidation-59dd` @ `46558a3`) onto `main` — do not reinvent architecture. +- Keep ADR 007: second image `NEXT_PUBLIC_BASE_PATH=/pro` on `127.0.0.1:3204`; root `:3200` unchanged; shared Postgres/Redis; Caddy `/pro*` without stripping. +- Auth.js: `NEXTAUTH_URL` origin-only + `AUTH_TRUST_HOST=true` (never put `/pro` in AUTH_URL). +- Do **not** redirect `evenslouis.pro` until Sprint 1–9 §5 smoke at `/pro` is finished. +- Do **not** start Architecture Phase 2–4. + +## What was done + +- Merged `/pro` onto `main` (`c81ac32` Merge PR #10). +- VPS checkout synced to `main`; Compose `pro` service present; containers healthy. +- Auth smoke inside `pro` container: CSRF → credentials callback → session → `/pro/dashboard` **200**. +- Public: `https://evenslouis.ca/pro/api/health` **200**. +- Doc/config sweep branch `cursor/pro-cutover-docs-59dd`: README, ROADMAP, PROJECT_CONTEXT, CHANGELOG, VPS/AFTER/BEFORE/DEPLOY checklists, `.env.example`, `scripts/smoke-test.sh` (public + `/pro`), `tests/e2e/prod.spec.ts` comment. + +## Insights + +- Live `/pro` was already healthy while `main` lacked the code — **merge drift** was the real risk; merging closed it. +- A checkout on an unrelated feature branch without Compose `pro` does not stop the existing `pro` container, but the next `docker compose up` from that tree would drift — keep VPS on `main` (or a branch that includes `pro`). + +## Next steps + +- Finish manual Sprint 1–9 §5 smoke at `/pro/...` URLs. +- Only then redirect `evenslouis.pro` → `https://evenslouis.ca/pro`. +- Merge remaining product PRs as needed (e.g. AI Brain Sonnet 5 / YouTube PR #13) without blocking `/pro` cut. +- Human decides when Architecture Phase 2 starts. diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 0c2a05a..59b17d0 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -1,60 +1,78 @@ #!/usr/bin/env bash -# Production smoke test: homepage, login, dashboard, /api/health, /api/ops/command. -# Usage: ./scripts/smoke-test.sh [BASE_URL] +# Production smoke test: public site + operator /pro app. +# Usage: ./scripts/smoke-test.sh [PUBLIC_URL] [PRO_URL] # Example: ./scripts/smoke-test.sh -# ./scripts/smoke-test.sh https://evenslouis.ca +# ./scripts/smoke-test.sh https://evenslouis.ca https://evenslouis.ca/pro +# +# Public site (root :3200): homepage, site lead form, /api/health +# Operator OS (/pro :3204): /pro/login, /pro/dashboard, /pro/api/health set -euo pipefail -BASE_URL="${1:-https://evenslouis.ca}" -BASE_URL="${BASE_URL%/}" +PUBLIC_URL="${1:-https://evenslouis.ca}" +PRO_URL="${2:-https://evenslouis.ca/pro}" +PUBLIC_URL="${PUBLIC_URL%/}" +PRO_URL="${PRO_URL%/}" FAIL=0 -echo "==> Smoke test: $BASE_URL" +echo "==> Smoke test" +echo " public: $PUBLIC_URL" +echo " pro: $PRO_URL" echo "" check() { - local path="$1" - local expected="$2" + local base="$1" + local path="$2" + local expected="$3" local code - code=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL$path" 2>/dev/null || echo "000") + code=$(curl -s -o /dev/null -w "%{http_code}" "$base$path" 2>/dev/null || echo "000") if [[ " $expected " == *" $code "* ]]; then - echo " OK GET $path -> $code" + echo " OK GET $base$path -> $code" else - echo " FAIL GET $path -> $code (expected one of: $expected)" + echo " FAIL GET $base$path -> $code (expected one of: $expected)" FAIL=1 fi } -check "/" "200" -check "/login" "200 302 307" -check "/dashboard" "200 302 307" -check "/api/ops/command" "401 200" +check_health() { + local base="$1" + local path="${2:-/api/health}" + local code body + code=$(curl -s -o /dev/null -w "%{http_code}" "$base$path" 2>/dev/null || echo "000") + body=$(curl -s "$base$path" 2>/dev/null || echo "") + if [ "$code" = "200" ] && echo "$body" | grep -q '"ok"[[:space:]]*:[[:space:]]*true'; then + echo " OK GET $base$path -> 200, ok: true" + else + echo " FAIL GET $base$path -> code=$code or ok not true" + FAIL=1 + fi +} -# Health: must be 200 and body contains "ok":true -code=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/health" 2>/dev/null || echo "000") -body=$(curl -s "$BASE_URL/api/health" 2>/dev/null || echo "") -if [ "$code" = "200" ] && echo "$body" | grep -q '"ok"[[:space:]]*:[[:space:]]*true'; then - echo " OK GET /api/health -> 200, ok: true" -else - echo " FAIL GET /api/health -> code=$code or ok not true" - FAIL=1 -fi +echo "-- public site (root) --" +check "$PUBLIC_URL" "/" "200" +check_health "$PUBLIC_URL" "/api/health" -# Site leads form (public): POST creates lead -code=$(curl -s -o /tmp/site-leads.json -w "%{http_code}" -X POST "$BASE_URL/api/site/leads" \ +# Site leads form (public root only) +code=$(curl -s -o /tmp/site-leads.json -w "%{http_code}" -X POST "$PUBLIC_URL/api/site/leads" \ -H "Content-Type: application/json" \ -d "{\"name\":\"Smoke\",\"email\":\"smoke-$(date +%s)@example.com\",\"message\":\"Smoke test\"}" 2>/dev/null || echo "000") if [ "$code" = "200" ] && grep -q '"ok"[[:space:]]*:[[:space:]]*true' /tmp/site-leads.json 2>/dev/null; then - echo " OK POST /api/site/leads -> 200, ok: true" + echo " OK POST $PUBLIC_URL/api/site/leads -> 200, ok: true" else - echo " FAIL POST /api/site/leads -> code=$code" + echo " FAIL POST $PUBLIC_URL/api/site/leads -> code=$code" FAIL=1 fi +echo "" +echo "-- operator OS (/pro) --" +check "$PRO_URL" "/login" "200 302 307" +check "$PRO_URL" "/dashboard" "200 302 307" +check "$PRO_URL" "/api/ops/command" "401 200" +check_health "$PRO_URL" "/api/health" + # Optional: SSL cert present (if https) -if [[ "$BASE_URL" == https://* ]]; then - host="${BASE_URL#https://}" +if [[ "$PUBLIC_URL" == https://* ]]; then + host="${PUBLIC_URL#https://}" host="${host%%/*}" if echo | openssl s_client -servername "$host" -connect "$host:443" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null; then echo " OK SSL $host" diff --git a/tests/e2e/prod.spec.ts b/tests/e2e/prod.spec.ts index a8cb509..d753280 100644 --- a/tests/e2e/prod.spec.ts +++ b/tests/e2e/prod.spec.ts @@ -1,7 +1,9 @@ /** * Production audit: health, DB checks, every page, key flows, silent fails, render speed. * - * Run against prod: E2E_ALLOW_MUTATIONS=1 USE_EXISTING_SERVER=1 PLAYWRIGHT_BASE_URL=https://evenslouis.ca E2E_EMAIL=... E2E_PASSWORD=... AGENT_CRON_SECRET= npm run test:e2e tests/e2e/prod.spec.ts + * Run against prod operator OS: + * E2E_ALLOW_MUTATIONS=1 USE_EXISTING_SERVER=1 PLAYWRIGHT_BASE_URL=https://evenslouis.ca/pro E2E_EMAIL=... E2E_PASSWORD=... AGENT_CRON_SECRET= npm run test:e2e tests/e2e/prod.spec.ts + * (NEXTAUTH_URL on server must be origin-only — https://evenslouis.ca — never with /pro) * * Local (existing server): Add AGENT_CRON_SECRET or RESEARCH_CRON_SECRET to .env (see .env.example) and restart dev server. */