Commit Graph

18 Commits

Author SHA1 Message Date
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