Commit Graph

27 Commits

Author SHA1 Message Date
Ho Ngoc Hai
25420720e7 fix(api,ci): remove type-only imports for DI and isolate CI ports from dev
- Remove `type` keyword from NestJS injectable class imports across all
  modules to fix runtime DI resolution (330+ handler/listener files)
- Offset CI docker-compose ports (5433/6380/8109/9002) to avoid
  conflicts with running dev containers
- Update .env.test, playwright.config.ts, and e2e workflow to use
  isolated CI ports with configurable overrides
- Fix prisma/seed.ts to use deterministic IDs for Prisma 7 upsert
  compatibility (phoneHash replaced phone as unique index)
- Add dedicated Docker bridge network for CI service containers

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 01:40:14 +07:00
Ho Ngoc Hai
1617921993 feat(payments): add Order & Escrow repository tests, prisma config, docs
Add 26 unit tests for PrismaOrderRepository and PrismaEscrowRepository
covering CRUD operations, pagination, domain mapping (bigint → Money),
idempotency key lookup, and escrow dispute workflow fields.

Update prisma.config.ts with dotenv import and seed command for Prisma 7.
Add architecture summary and codebase overview documentation files.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-13 00:36:49 +07:00
Ho Ngoc Hai
2c97f99214 feat(payments): add Order & Escrow entities with CQRS commands, Prisma schema
- Add Order entity with lifecycle (pending → paid → completed/cancelled/refunded)
- Add Escrow entity with hold/release/dispute flow for secure transactions
- Add PlatformFee value object with tiered commission calculation
- Implement CQRS: CreateOrder, CancelOrder, HoldEscrow, ReleaseEscrow commands
- Add GetOrderStatus query handler
- Add OrdersController with REST endpoints and DTOs
- Add Prisma models for Order, Escrow, EscrowStatusHistory
- Add domain event classes for order and escrow state changes
- Add unit tests for Order, Escrow entities and PlatformFee VO
- Update PROJECT_TRACKER to Wave 14 status

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-12 23:40:00 +07:00
Ho Ngoc Hai
1fbe2f4e73 feat: add MFA/TOTP auth, PII encryption, agents/leads/inquiries modules, and comprehensive tests
- Add TOTP-based MFA with setup, verify, disable, backup codes, and challenge flow
- Add PII field encryption middleware with AES-256-GCM and deterministic search hashes
- Add agents, inquiries, and leads domain modules with entities, events, value objects
- Add web dashboard pages for inquiries and leads with detail dialogs
- Add 30+ component tests (valuation, charts, listings, search, providers, UI)
- Add Prisma migrations for encryption hash columns and MFA TOTP support
- Fix all ESLint errors (unused imports, duplicate imports, lint auto-fixes)
- Update dependencies and lock file
- Clean up obsolete exploration/QA docs, add audit documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 23:43:20 +07:00
Ho Ngoc Hai
a59bf8eda2 feat(infra): add web vitals Grafana dashboard and admin audit log migration
- Add Grafana dashboard for web vitals metrics visualization
- Add Prisma migration for admin audit log table

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:39:37 +07:00
Ho Ngoc Hai
45e48c063c fix(db): add explicit onDelete strategies to all Prisma FK relations
Audit and update all foreign key relations in schema.prisma with
appropriate cascade/restrict/set-null strategies to prevent orphaned
records and FK constraint violations on parent deletion.

Changes (RESTRICT → CASCADE):
- Agent.userId, Listing.propertyId, Transaction.listingId
- Inquiry.listingId, Inquiry.userId, Lead.agentId
- Subscription.userId, UsageRecord.subscriptionId
- Valuation.propertyId, Review.userId

Confirmed correct (no change needed):
- Listing.agentId (SetNull), Listing.sellerId (Restrict)
- Transaction.buyerId (Restrict), Payment.userId (Restrict)
- Payment.transactionId (SetNull), Subscription.planId (Restrict)
- PropertyMedia, SavedSearch, RefreshToken, OAuthAccount (CASCADE)

Migration: 20260411000000_add_cascade_delete_strategies

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:21:46 +07:00
Ho Ngoc Hai
05abbc5250 feat(infra): add PgBouncer connection pooling for production PostgreSQL
Introduces PgBouncer as a connection pooler between the API service and
PostgreSQL in docker-compose.prod.yml, reducing connection overhead and
improving concurrency under production load.

- Add PgBouncer service (edoburu/pgbouncer:1.23.1-p2) with transaction
  pool mode, max_client_conn=200, default_pool_size=20
- Route API DATABASE_URL through PgBouncer (port 6432), keep direct
  connection (DATABASE_URL_DIRECT) for Prisma migrations/introspection
- Create infra/pgbouncer/ config: pgbouncer.ini, userlist template,
  and entrypoint script with runtime env-var substitution
- Update prisma.config.ts to prefer DATABASE_URL_DIRECT for migrations
- Add K6 load test (e2e/load/pgbouncer-pool-test.js) with ramp-up to
  200 VUs, pool exhaustion detection, and p95 < 2s threshold
- Add PgBouncer env vars to .env.example

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:15:21 +07:00
Ho Ngoc Hai
e03c4699d0 feat(api): implement GDPR-compliant user data deletion
- Add deletedAt/deletionScheduledAt fields to User model with indexes
- Implement 5 CQRS command handlers:
  - RequestUserDeletion: 30-day soft-delete grace period
  - CancelUserDeletion: restore within grace period
  - ForceDeleteUser: admin immediate deletion with PII anonymization
  - ProcessScheduledDeletions: cron-ready batch processor
  - ExportUserData: GDPR Article 20 data portability
- Cascade strategy: anonymize PII, expire listings, cancel subscriptions,
  delete reviews/inquiries/searches/notifications, preserve payments for audit
- Add UserDataController with DELETE /users/me, POST /users/me/cancel-deletion,
  GET /users/me/export, DELETE /users/:id/force (admin)
- 22 unit tests covering all handlers (160 files, 853 tests passing)
- Migration: 20260410000000_add_user_soft_delete_fields

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 05:43:54 +07:00
Ho Ngoc Hai
ef47d9eb80 chore(db): add query indexes migration and update project config
- Add database migration for missing query indexes on frequently filtered columns
- Update Prisma schema
- Update .env.example, eslint config, and dependency-cruiser config

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:44:37 +07:00
Ho Ngoc Hai
45ebc6cf1d feat: API versioning, compound indexes, and new exports
- Add global /api/v1/ prefix with health/ready exclusions
- Add compound indexes on Property and Listing for query optimization
- Export CsrfMiddleware and UploadedFile type from shared infra
- New Prisma migration for compound indexes

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 01:27:17 +07:00
Ho Ngoc Hai
6f3e6998ac feat(notifications): complete notification delivery system with email, push, and in-app support
Add 5 new event listeners (listing.approved, listing.rejected, payment.confirmed,
subscription.expiring, inquiry.received), 3 new Handlebars templates, readAt field
for in-app read/unread tracking, unread/mark-as-read API endpoints, and unit tests.

All 57 notification tests pass, lint clean, typecheck clean.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 00:11:34 +07:00
Ho Ngoc Hai
0abf9df84e fix(db): add missing Review.userId index for FK query performance
The Review table was missing an index on userId, causing full table scans
when querying reviews by user. All other FK columns across 22 models were
verified to have proper indexes already (including Listing.sellerId which
was added in a prior migration).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 22:43:59 +07:00
Ho Ngoc Hai
3864f78405 feat(subscriptions): implement subscription quota enforcement
- Apply QuotaGuard + @RequireQuota to listing creation and analytics endpoints
- Add QuotaExceeded domain event emitted when quota is exceeded
- Create ListingCreatedUsageHandler to auto-meter usage on listing creation
- Create QuotaExceededListener to send email notifications on quota exceeded
- Add maxAnalyticsQueries and maxMediaUploads fields to Plan model
- Add quota.exceeded email notification template
- Define quota limits per plan tier in seed data
- Add 15 unit tests covering guard, event handler, listener, and event

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 14:16:32 +07:00
Ho Ngoc Hai
cc5c81904b fix(lint): resolve all 49 lint warnings and errors across codebase
- Remove unused imports/variables in seed scripts and test files
- Replace console.log with console.warn in seed/utility scripts
- Replace `as any` with proper Prisma types (InputJsonValue, PaymentStatus, Plan, UserWhereInput)
- Fix import-x/no-named-as-default-member warnings in logger, mapbox, eslint config
- Prefix unused callback params with underscore in e2e tests

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:22:07 +07:00
Ho Ngoc Hai
47c34f129e fix(db): add LeadStatus enum, Transaction.buyerId FK, Inquiry compound index
- Convert Lead.status from String to LeadStatus enum (NEW, CONTACTED, QUALIFIED, NEGOTIATING, CONVERTED, LOST) with safe data migration
- Add FK constraint Transaction.buyerId -> User.id to prevent orphaned transactions
- Add compound index on Inquiry(listingId, userId) for duplicate inquiry checks

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:17:24 +07:00
Ho Ngoc Hai
af71270a2e feat: upgrade major dependencies to latest versions
- Prisma 6.19 → 7.7 (driver adapter pattern, prisma.config.ts)
- TypeScript 5.9 → 6.0 (ignoreDeprecations, CSS type declarations)
- Vitest 3.2 → 4.1
- Pino 9.14 → 10.3
- @types/node 22.x → 25.x

All 307 tests pass, typecheck clean, build succeeds.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:15:36 +07:00
Ho Ngoc Hai
91ef71d5e1 fix(db): add missing indexes, bound unbounded queries, parallelize admin queries
- Add 7 missing indexes: User(kycStatus, isActive, createdAt),
  Listing(createdAt, featuredUntil, expiresAt), Payment(createdAt)
- Add take:50 limit to unbounded findMediaByPropertyId and findByPropertyId
- Parallelize sequential queries in getUserDetail with Promise.all

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:10:39 +07:00
Ho Ngoc Hai
91b76d567b fix(api): add JWT scheme to @ApiBearerAuth and fix Prisma 7 extensions config
- Add 'JWT' scheme name to @ApiBearerAuth() in payments & subscriptions
  controllers so Swagger UI correctly links to the JWT security definition
- Add postgresqlExtensions preview feature to Prisma schema for v7 compat

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:08:03 +07:00
Ho Ngoc Hai
0c227b6b01 fix: resolve typecheck errors in seed scripts
Add non-null assertions for array indexing in prisma/seed.ts and
scripts/seed-districts.ts to satisfy strict TypeScript checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 12:45:17 +07:00
Ho Ngoc Hai
2502aa69b7 fix: production readiness — resolve build, lint, and code quality issues
- Fix Next.js build failure: remove duplicate route at (dashboard)/listings/[id]
  that conflicted with (public)/listings/[id] (same URL path in two route groups)
- Fix 772 ESLint errors: auto-fix import ordering (import-x/order), remove unused
  imports/variables, convert empty interfaces to type aliases, replace require()
  with ESM imports, fix consistent-type-imports violations
- Add CLAUDE.md for developer onboarding documentation
- All checks pass: 0 lint errors, typecheck clean, 230 tests passing, build success

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 07:15:06 +07:00
Ho Ngoc Hai
9583d1cb66 fix(payments): harden payment flow with idempotency keys, amount validation, and magic byte file validation
- Add dedicated idempotencyKey column with unique constraint (userId, provider, idempotencyKey) to prevent duplicate payments at DB level
- Add @Min(1) @Max(100B) validators on amountVND in CreatePaymentDto to reject invalid amounts at API boundary
- Replace read-check-write callback handler with atomic updateIfStatus to eliminate race condition on concurrent callbacks
- Add magic byte verification in FileValidationPipe to validate file content matches declared MIME type server-side

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 06:18:26 +07:00
Ho Ngoc Hai
51c6eed565 feat(seed): add standalone seed scripts for districts, plans, and market data
- scripts/seed-districts.ts: Vietnam district/ward data for HCM, Hanoi, Da Nang with sample properties
- scripts/seed-plans.ts: Subscription plans (FREE, AGENT_PRO, INVESTOR, ENTERPRISE)
- scripts/import-market-data.ts: Market index data across all 3 cities with realistic pricing
- All scripts are idempotent (upsert/ON CONFLICT DO NOTHING)
- Refactored prisma/seed.ts to import shared data from scripts, removing duplication

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 05:10:05 +07:00
Ho Ngoc Hai
f55c8a8788 feat(db): add missing FK indexes on Listing, Payment, Subscription
- Add @@index([sellerId]) and @@index([propertyId]) to Listing model
- Add @@index([transactionId]) to Payment model
- Add @@index([planId]) to Subscription model
- Prevents full table scans on frequently-queried foreign keys

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 04:02:13 +07:00
Ho Ngoc Hai
ff358f6148 feat(db): add initial Prisma migration and seed script
- Add init migration with all 23 models including PostGIS
- Add seed script for districts, plans, and sample data

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 02:04:30 +07:00
Ho Ngoc Hai
0b29fac35e feat(notifications): add multi-channel notification module with Email, FCM, templates, and event listeners
- Domain: NotificationLog/NotificationPreference entities, repositories, channel value object
- Infrastructure: EmailService (nodemailer/SMTP), FcmService (firebase-admin), TemplateService (Handlebars)
- Application: SendNotification CQRS command, UserRegistered + AgentVerified event listeners
- Presentation: NotificationsController with history, preferences, and templates endpoints
- Prisma: NotificationLog and NotificationPreference models with proper indexes
- Templates: Vietnamese notification templates for user.registered, agent.verified, listing.approved, inquiry.received, password.reset

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 01:42:17 +07:00
Ho Ngoc Hai
391c040100 feat(auth): implement Auth module with register, login, JWT, guards, and CQRS
- Add RefreshToken and OAuthAccount models to Prisma schema
- Implement clean architecture: domain (entities, VOs, events, repo interfaces),
  infrastructure (Prisma repos, Passport strategies, token service),
  application (CQRS command/query handlers), presentation (controller, guards, DTOs)
- Endpoints: POST /auth/register, /auth/login, /auth/refresh, GET /auth/profile,
  GET /auth/profile/agent, PATCH /auth/kyc
- JWT access + refresh token rotation with family-based revocation
- Role-based guards (BUYER, SELLER, AGENT, ADMIN)
- 16 unit tests (value objects, entity) + integration test suite
- All 80 tests passing, clean TypeScript build

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 00:24:42 +07:00
Ho Ngoc Hai
e1e5fa6252 feat: scaffold monorepo with Turborepo + NestJS + Next.js
- Turborepo monorepo with pnpm workspaces
- apps/api: NestJS 11.x with CQRS module
- apps/web: Next.js 14 App Router + TailwindCSS
- src/modules/shared: base entities, Result pattern, value objects
- TypeScript 5.7+ strict mode, shared tsconfig base
- Build pipeline: dev, build, lint, test, typecheck

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-07 23:52:33 +07:00