Files
HOA_Financial_Platform/CLAUDE.md
olsch01 06bc0181f8 feat: add k6 load testing suite, NRQL query library, and CLAUDE.md
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>
2026-03-19 15:49:22 -04:00

9.5 KiB
Raw Blame History

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)
Email 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 extracts orgId from JWT, sets tenant schema (60s cache)
  • JwtAuthGuard Passport JWT guard on all protected routes
  • WriteAccessGuard blocks write ops for viewer role and past_due orgs
  • @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 SharedArray for user pool, http.batch() for parallel requests
  • Custom metrics: *_duration trends + *_error_rate rates 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.ts co-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 build in backend/ after changes to verify compilation
  • TypeORM entities use decorators (@Entity, @Column, etc.)
  • Multi-tenant: any new module touching tenant data must use TenantService to get the correct schema connection
  • New endpoints need @UseGuards(JwtAuthGuard) and should respect WriteAccessGuard
  • Use @AllowViewer() on read-only endpoints