6 KiB
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 pullon the server - The server has NO
.envin git — secrets are in/opt/performancewest/.envand/opt/performancewest-dev/.envon 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/tracksorigin/main - Dev:
/opt/performancewest-dev/tracksorigin/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 3002performancewest-dev-site-1— port 4323performancewest-dev-workers-1— port 8090 (internal)performancewest-dev-api-postgres-1— port 5433- Dev connects to
performancewest_defaultnetwork for ERPNext, MinIO, Listmonk, Ollama
Dev uses Stripe test mode
NODE_ENV=development→ API auto-selectsSTRIPE_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 handlerssite/— Astro static sitesite/src/pages/— Astro pages (compile to HTML)site/src/components/— Astro componentssite/public/— Static files served as-is (tools, order pages)site/public/_astro/— Astro build artifacts (hoisted JS) — do NOT edit these directlyscripts/— Python workers, document generators, scrapersscripts/workers/services/— Compliance service handlers (one per service)scripts/workers/job_server.py— Main worker job dispatchscripts/document_gen/templates/— DOCX/PDF generatorsscripts/formation/— State formation adapters, entity cacheinfra/— Ansible playbooks, nginx configsdocs/— Product documentation, research
Key Patterns
Service catalog
Services are defined in api/src/routes/compliance-orders.ts → COMPLIANCE_SERVICES dict. Each service needs:
- Entry in
COMPLIANCE_SERVICES(slug, name, price, erpnext_item) - Handler class in
scripts/workers/services/registered in__init__.py→SERVICE_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.jswhich is insite/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 pagesite/public/order/neca-ocn/index.html— NECA OCN order page- These use the 2-step checkout: POST
/api/v1/compliance-ordersor/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:
sendComplianceIntakeEmailin checkout.ts (DO NOT also sendsendOrderConfirmationEmailfor batches) - Intake emails from handlers: deduped per batch — only first order (lowest order_number) sends
- RMD review email: sent by
rmd_filing.pyhandler after filing is assembled