new-site/CLAUDE.md
justin 6b569b52fe Update CLAUDE.md with complete deployment guide, infrastructure map, and key patterns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 02:54:44 -05:00

6 KiB

Performance West — Development Guidelines

Deployment Rules

  • NEVER edit files in /tmp/ — always edit in this project directory
  • NEVER scp individual files to dev/prod — always commit and deploy via git
  • After editing any file, commit it: git add <file> && git commit -m "description"
  • All source code lives in this repo and is deployed via git pull on the server
  • The server has NO .env in git — secrets are in /opt/performancewest/.env and /opt/performancewest-dev/.env on the server

Deploy workflow

# 1. Make changes locally
# 2. Commit and push
git add -A && git commit -m "description" && git push origin main

# 3. SSH to server and deploy
ssh -p 22022 deploy@207.174.124.71
cd /opt/performancewest && ./deploy.sh          # rebuild all (site, api, workers)
cd /opt/performancewest && ./deploy.sh site     # rebuild just site
cd /opt/performancewest && ./deploy.sh api      # rebuild just api

Deploy to dev

ssh -p 22022 deploy@207.174.124.71
cd /opt/performancewest-dev
git pull origin main
docker compose build site api workers
docker compose up -d site api workers

Run migrations

# Copy SQL into container and run via node
docker cp api/migrations/074_xxx.sql performancewest-api-1:/tmp/
docker exec performancewest-api-1 node -e "const fs=require('fs');const{Pool}=require('pg');const p=new Pool({connectionString:process.env.DATABASE_URL});p.query(fs.readFileSync('/tmp/074_xxx.sql','utf8')).then(()=>{console.log('OK');p.end()}).catch(e=>{console.log(e.message);p.end()})"

Git Server

  • Remote: ssh://git@git.performancewest.net:2222/justin/new-site.git
  • Branch: main
  • Prod: /opt/performancewest/ tracks origin/main
  • Dev: /opt/performancewest-dev/ tracks origin/main

Infrastructure

  • Prod server: deploy@207.174.124.71:22022/opt/performancewest/
  • Dev server: same host → /opt/performancewest-dev/
  • HestiaCP: root@cp.carrierone.com:22022 (DNS, email provisioning)
  • Docker Compose: prod has full stack (api, site, workers, postgres, erpnext, minio, listmonk, umami, ollama); dev has api/site/workers/postgres + connects to prod network for shared services

Prod containers

  • performancewest-api-1 — Express API (port 3001)
  • performancewest-site-1 — Astro static site (port 4322)
  • performancewest-workers-1 — Python job server (port 8090)
  • performancewest-api-postgres-1 — PostgreSQL (port 5432)
  • performancewest-erpnext-1 — ERPNext CRM (port 8080)
  • performancewest-minio-1 — MinIO object storage (port 9000)
  • performancewest-listmonk-1 — Email marketing (port 9100)
  • performancewest-ollama-1 — LLM (port 11434)
  • performancewest-umami-1 — Analytics (port 3100)

Dev containers

  • performancewest-dev-api-1 — port 3002
  • performancewest-dev-site-1 — port 4323
  • performancewest-dev-workers-1 — port 8090 (internal)
  • performancewest-dev-api-postgres-1 — port 5433
  • Dev connects to performancewest_default network for ERPNext, MinIO, Listmonk, Ollama

Dev uses Stripe test mode

  • NODE_ENV=development → API auto-selects STRIPE_TEST_SECRET_KEY
  • Test card: 4242 4242 4242 4242, any future expiry, any CVC
  • PayPal sandbox: api-m.sandbox.paypal.com

Project Structure

  • api/ — Express.js API (TypeScript)
  • api/migrations/ — SQL migration files (run manually, numbered 001-074+)
  • api/src/routes/ — API route handlers
  • site/ — Astro static site
  • site/src/pages/ — Astro pages (compile to HTML)
  • site/src/components/ — Astro components
  • site/public/ — Static files served as-is (tools, order pages)
  • site/public/_astro/ — Astro build artifacts (hoisted JS) — do NOT edit these directly
  • scripts/ — Python workers, document generators, scrapers
  • scripts/workers/services/ — Compliance service handlers (one per service)
  • scripts/workers/job_server.py — Main worker job dispatch
  • scripts/document_gen/templates/ — DOCX/PDF generators
  • scripts/formation/ — State formation adapters, entity cache
  • infra/ — Ansible playbooks, nginx configs
  • docs/ — Product documentation, research

Key Patterns

Service catalog

Services are defined in api/src/routes/compliance-orders.tsCOMPLIANCE_SERVICES dict. Each service needs:

  • Entry in COMPLIANCE_SERVICES (slug, name, price, erpnext_item)
  • Handler class in scripts/workers/services/ registered in __init__.pySERVICE_HANDLERS
  • ERPNext Item created in the CRM

Intake pages

  • Astro pages at site/src/pages/order/<slug>.astro — these are POST-PAYMENT intake forms
  • Do NOT show prices on intake pages — the client has already paid
  • Steps defined in site/src/lib/intake_manifest.ts
  • The "payment" step in the manifest is for standalone orders only; batch orders skip it

Static tool pages

  • site/public/tools/fcc-compliance-check/index.html — static HTML, NOT an Astro page
  • Uses /_astro/hoisted.aBLqmOPy.js which is in site/public/_astro/ (passed through by Astro build)
  • Enhancement script in the inline <script> at the bottom of the HTML
  • Tailwind classes from public/ files may not be in the compiled CSS — use inline styles for dynamic elements

Order pages

  • site/public/order/fcc-compliance/index.html — batch compliance checkout (static HTML)
  • site/public/order/state-puc/index.html — State PUC order page
  • site/public/order/neca-ocn/index.html — NECA OCN order page
  • These use the 2-step checkout: POST /api/v1/compliance-orders or /batch → POST /api/v1/checkout/create-session

Cron jobs

  • Defined in infra/ansible/roles/worker-crons/defaults/main.yml
  • Deployed as systemd timers via infra/ansible/playbooks/deploy-crons.yml
  • Run inside the workers container: docker compose exec -T workers python -m <module>

Emails

  • Batch orders: sendComplianceIntakeEmail in checkout.ts (DO NOT also send sendOrderConfirmationEmail for batches)
  • Intake emails from handlers: deduped per batch — only first order (lowest order_number) sends
  • RMD review email: sent by rmd_filing.py handler after filing is assembled