Add comprehensive load testing infrastructure: - k6 auth-dashboard flow (login → profile → dashboard KPIs → widgets → refresh → logout) - k6 CRUD flow (units, vendors, journal entries, payments, reports) - Environment configs with staging/production/local thresholds - Parameterized user pool CSV matching app roles - New Relic NRQL query library (25+ queries for perf analysis) - Empty baseline.json structure for all tested endpoints - CLAUDE.md documenting full stack, auth, route map, and conventions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.5 KiB
CLAUDE.md – HOA Financial Platform (HOALedgerIQ)
Project Overview
Multi-tenant SaaS platform for HOA (Homeowners Association) financial management. Handles chart of accounts, journal entries, budgets, invoices, payments, reserve planning, and board scenario planning.
Stack & Framework
| Layer | Technology |
|---|---|
| Backend | NestJS 10 (TypeScript), runs on port 3000 |
| Frontend | React 18 + Vite 5 + Mantine UI + Zustand |
| Database | PostgreSQL via TypeORM 0.3 |
| Cache | Redis (BullMQ for queues) |
| Auth | Passport.js – JWT access + httpOnly refresh |
| Payments | Stripe (checkout, subscriptions, webhooks) |
| Resend | |
| AI | NVIDIA API (Qwen model) for investment advisor |
| Monitoring | New Relic APM (app name: HOALedgerIQ_App) |
| Infra | Docker Compose (dev + prod), Nginx reverse proxy |
Auth Pattern
- Access token: JWT, 1-hour TTL, payload
{ sub, email, orgId, role, isSuperadmin } - Refresh token: 64-byte random, SHA256-hashed in DB, 30-day TTL, sent as httpOnly cookie
ledgeriq_rt - MFA: TOTP via
otplib, challenge token (5-min TTL), recovery codes - Passkeys: WebAuthn via
@simplewebauthn/server - SSO: Google OAuth 2.0, Azure AD
- Password hashing: bcryptjs, cost 12
- Rate limiting: 100 req/min global (Throttler), custom per endpoint
Guards & Middleware
TenantMiddleware– extractsorgIdfrom JWT, sets tenant schema (60s cache)JwtAuthGuard– Passport JWT guard on all protected routesWriteAccessGuard– blocks write ops forviewerrole andpast_dueorgs@AllowViewer()decorator – exempts read endpoints from WriteAccessGuard
Roles
president, treasurer, secretary, member_at_large, manager, homeowner, admin, viewer
Multi-Tenant Architecture
- Shared schema (
shared): users, organizations, user_organizations, refresh_tokens, invite_tokens, login_history, cd_rates - Tenant schemas (dynamic, per org): accounts, journal_entries, budgets, invoices, payments, units, vendors, etc.
- Schema name stored in
shared.organizations.schema_name
Route Map (180+ endpoints)
Auth (/api/auth)
| Method | Path | Purpose |
|---|---|---|
| POST | /login | Email/password login |
| POST | /refresh | Refresh access token (cookie) |
| POST | /logout | Revoke refresh token |
| POST | /logout-everywhere | Revoke all sessions |
| GET | /profile | Current user profile |
| POST | /register | Register (disabled by default) |
| POST | /activate | Activate invited user |
| POST | /forgot-password | Request password reset |
| POST | /reset-password | Reset with token |
| PATCH | /change-password | Change password (authed) |
| POST | /switch-org | Switch active organization |
Auth MFA (/api/auth/mfa)
| POST | /setup | POST /enable | POST /verify | POST /disable | GET /status |
Auth Passkeys (/api/auth/passkeys)
| POST /register-options | POST /register | POST /login-options | POST /login | GET / | DELETE /:id |
Admin (/api/admin) – superadmin only
| GET /metrics | GET /users | GET /organizations | PUT /organizations/:id/subscription | POST /impersonate/:userId | POST /tenants |
Organizations (/api/organizations)
| POST / | GET / | PATCH /settings | GET /members | POST /members | PUT /members/:id/role | DELETE /members/:id |
Accounts (/api/accounts)
| GET / | GET /trial-balance | POST / | PUT /:id | PUT /:id/set-primary | POST /bulk-opening-balances | POST /:id/opening-balance | POST /:id/adjust-balance |
Journal Entries (/api/journal-entries)
| GET / | GET /:id | POST / | POST /:id/post | POST /:id/void |
Budgets (/api/budgets)
| GET /:year | PUT /:year | GET /:year/vs-actual | POST /:year/import | GET /:year/template |
Invoices (/api/invoices)
| GET / | GET /:id | POST /generate-preview | POST /generate-bulk | POST /apply-late-fees |
Payments (/api/payments)
| GET / | GET /:id | POST / | PUT /:id | DELETE /:id |
Units (/api/units)
| GET / | GET /:id | POST / | PUT /:id | DELETE /:id | GET /export | POST /import |
Vendors (/api/vendors)
| GET / | GET /:id | POST / | PUT /:id | GET /export | POST /import | GET /1099-data |
Reports (/api/reports)
| GET /dashboard | GET /balance-sheet | GET /income-statement | GET /cash-flow | GET /cash-flow-sankey | GET /aging | GET /year-end | GET /cash-flow-forecast | GET /quarterly |
Board Planning (/api/board-planning)
Scenarios CRUD, scenario investments, scenario assessments, projections, budget plans – 28 endpoints total.
Other Modules
/api/fiscal-periods– list, close, lock/api/reserve-components– CRUD/api/capital-projects– CRUD/api/projects– CRUD + planning + import/export/api/assessment-groups– CRUD + summary + default/api/monthly-actuals– GET/POST /:year/:month/api/health-scores– latest + calculate/api/investment-planning– snapshot, market-rates, recommendations/api/investment-accounts– CRUD/api/attachments– upload, list, download, delete (10MB limit)/api/onboarding– progress get/patch/api/billing– trial, checkout, webhook, subscription, portal
Database
- Connection pool: min 5, max 30, 30s idle, 5s connect timeout
- Migrations: SQL files in
db/migrations/(manual execution, no ORM runner) - Init script:
db/init/00-init.sql(shared schema DDL)
Key File Paths
| Purpose | Path |
|---|---|
| NestJS bootstrap | backend/src/main.ts |
| Root module | backend/src/app.module.ts |
| Auth controller | backend/src/modules/auth/auth.controller.ts |
| Auth service | backend/src/modules/auth/auth.service.ts |
| Refresh token svc | backend/src/modules/auth/refresh-token.service.ts |
| JWT strategy | backend/src/modules/auth/strategies/jwt.strategy.ts |
| Tenant middleware | backend/src/database/tenant.middleware.ts |
| Write-access guard | backend/src/common/guards/write-access.guard.ts |
| DB schema init | db/init/00-init.sql |
| Env example | .env.example |
| Docker compose (dev) | docker-compose.yml |
| Frontend entry | frontend/src/main.tsx |
| Frontend pages | frontend/src/pages/ |
Environment Variables (critical)
DATABASE_URL – PostgreSQL connection string
REDIS_URL – Redis connection
JWT_SECRET – JWT signing key
INVITE_TOKEN_SECRET – Invite token signing
STRIPE_SECRET_KEY – Stripe API key
STRIPE_WEBHOOK_SECRET – Stripe webhook verification
RESEND_API_KEY – Email service
NEW_RELIC_APP_NAME – "HOALedgerIQ_App"
NEW_RELIC_LICENSE_KEY – New Relic license
APP_URL – Base URL for email links
New Relic
- App name:
HOALedgerIQ_App(env:NEW_RELIC_APP_NAME) - Enabled via
NEW_RELIC_ENABLED=true - NRQL query library:
load-tests/analysis/nrql-queries.sql
Load Testing
Run k6 scenarios
# Auth + Dashboard flow (staging)
k6 run --env TARGET_ENV=staging load-tests/scenarios/auth-dashboard-flow.js
# CRUD flow (staging)
k6 run --env TARGET_ENV=staging load-tests/scenarios/crud-flow.js
# Local dev
k6 run --env TARGET_ENV=local load-tests/scenarios/auth-dashboard-flow.js
Conventions
- Scenarios live in
load-tests/scenarios/ - Config in
load-tests/config/environments.json(staging/production/local thresholds) - Test users parameterized from
load-tests/config/user-pool.csv - Baseline results stored in
load-tests/analysis/baseline.json - NRQL queries for New Relic in
load-tests/analysis/nrql-queries.sql - All k6 scripts use
SharedArrayfor user pool,http.batch()for parallel requests - Custom metrics:
*_durationtrends +*_error_raterates per journey - Thresholds: p95 latency + error rate per environment
User Pool CSV Format
email,password,orgId,role
Roles match the app: treasurer, admin, president, manager, member_at_large, viewer, homeowner
Fix Conventions
- Backend tests:
npm run test(Jest,*.spec.tsco-located with source) - E2E tests:
npm run test:e2e - Backend build:
npm run build(NestJS CLI) - Frontend dev:
npm run dev(Vite, port 5173) - Frontend build:
npm run build - Always run
npm run buildinbackend/after changes to verify compilation - TypeORM entities use decorators (
@Entity,@Column, etc.) - Multi-tenant: any new module touching tenant data must use
TenantServiceto get the correct schema connection - New endpoints need
@UseGuards(JwtAuthGuard)and should respectWriteAccessGuard - Use
@AllowViewer()on read-only endpoints