129 lines
6 KiB
Markdown
129 lines
6 KiB
Markdown
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
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
|
|
```bash
|
|
# 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.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.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
|