Commit Graph

150 Commits

Author SHA1 Message Date
Ho Ngoc Hai
18e50a9649 fix(api): add error handling to remaining 51 CQRS handlers across 8 modules
Wraps every handler's execute() method in a try-catch block that:
- Re-throws DomainExceptions to preserve structured error responses
- Logs unexpected infrastructure errors with full context
- Throws InternalServerErrorException with Vietnamese user message

Modules updated:
- auth (11 handlers: register, refresh-token, verify-kyc, deletions, profile queries)
- listings (7 handlers: create, moderate, upload, status, search, queries)
- payments (5 handlers: create, callback, refund, status, transactions)
- subscriptions (7 handlers: create, cancel, upgrade, meter, quota, billing, plans)
- analytics (8 handlers: reports, events, market-index, district, heatmap, trends, valuation)
- search (9 handlers: saved-search CRUD, reindex, sync, geo-search, properties)
- notifications (1 handler: send-notification)
- agents (3 handlers: quality-score, dashboard, public-profile)

Combined with the previous commit (29 handlers in admin, inquiries, leads, reviews),
all 80+ CQRS handlers now have comprehensive error handling.

Verification:
- pnpm typecheck: 0 errors
- pnpm test: 1387 tests passed (228 files)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 20:04:42 +07:00
Ho Ngoc Hai
7008230424 fix(auth): prevent login endpoint from returning 500 on invalid credentials
LocalStrategy.validate lacked a try-catch, so infrastructure errors
(DB timeouts, bcrypt failures, null/undefined phone) escaped as raw
Error instances. LocalAuthGuard.handleRequest blindly re-threw them,
causing GlobalExceptionFilter to map them to 500 Internal Server Error
instead of 401 Unauthorized.

Changes:
- Add null/falsy guard for phone and password in LocalStrategy.validate
- Wrap validate body in try-catch; re-throw DomainExceptions, wrap
  unexpected errors as UnauthorizedException (401)
- Add error type-checking in LocalAuthGuard.handleRequest: re-throw
  HttpException subclasses directly, wrap other errors as 401
- Add @IsNotEmpty() validators to LoginDto for Swagger accuracy
- Add 5 new test cases covering undefined/null/empty inputs, DB
  errors, and bcrypt failures
- Update guard tests for the new type-checking behaviour

Resolves TEC-1841

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 19:53:41 +07:00
Ho Ngoc Hai
2da333a95b fix(api): add error handling to 29 CQRS handlers in admin, inquiries, leads, reviews
Add standardized try-catch error handling pattern to all command and
query handlers in the four priority modules:
- admin (15 handlers): commands + queries, added LoggerService injection
- inquiries (4 handlers): commands + queries
- leads (5 handlers): commands + queries
- reviews (5 handlers): commands + queries

Each handler now:
- Wraps execute() in try-catch
- Re-throws DomainException subclasses (NotFoundException, etc.)
- Logs infrastructure errors via LoggerService
- Throws InternalServerErrorException for unexpected failures

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 19:35:21 +07:00
Ho Ngoc Hai
80725ed81f feat(notifications): add saved search email alert templates
Add the two missing Handlebars templates (saved_search_alert and
saved_search_digest) that are referenced by the real-time event handler
and daily digest cron but were never defined, causing a runtime crash.
Includes corresponding unit tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:59:03 +07:00
Ho Ngoc Hai
f8f2935f45 test(api): add unit tests for MCP, Inquiries, and Leads modules
Increase test file coverage to ≥50% for three under-tested modules:

- MCP: +1 test (mcp.module.spec.ts) → 2/2 files covered (100%)
- Inquiries: +4 tests (events, repository contract, prisma repo, DTOs)
  → 10/18 files covered (55.6%)
- Leads: +4 tests (events, repository contract, prisma repo, DTOs)
  → 12/22 files covered (54.5%)

All 225 test files pass with 1353 tests total.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:47:25 +07:00
Ho Ngoc Hai
40832a9d12 fix(api): resolve 2 TypeScript compile errors
- Use bracket notation for process.env['BCRYPT_ROUNDS'] index signature access
- Remove redundant route? property from AuthenticatedRequest interface
  that conflicted with Express Request's required route property

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:42:23 +07:00
Ho Ngoc Hai
4372a9ee12 chore: update package dependencies and Playwright config
Update root, API, and web package.json files with latest dependencies.
Refresh pnpm-lock.yaml and update Playwright configuration.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:40:59 +07:00
Ho Ngoc Hai
9914d02439 chore(web): update Next.js config, Tailwind config, and type definitions
Sync next-env.d.ts, update next.config.js and tailwind.config.ts with
latest settings, and refresh tsconfig build info.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:40:30 +07:00
Ho Ngoc Hai
1b86c5bf2c fix(web): update search, listing, and map components
Improve agent profile client, comparison table, image gallery/upload,
listing map, filter bar, property card, and search results components
with better error handling, type safety, and UX refinements.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:40:15 +07:00
Ho Ngoc Hai
759052a71f fix(web): update dashboard pages, layouts, and listing forms
Update 12 page/layout files across auth, dashboard, listings, and search
routes to improve type safety, fix component imports, and align with
latest API changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:39:59 +07:00
Ho Ngoc Hai
8ca64e3267 feat(web): add saved searches, image lightbox, and web vitals tracking
New features:
- Saved searches dashboard page with CRUD hooks and API client
- Image lightbox component for property gallery full-screen viewing
- Web vitals provider and reporting utilities for performance monitoring
- Image blur placeholder generation utility

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:39:22 +07:00
Ho Ngoc Hai
97c7d58f5e test(api): add unit tests for admin, leads, and reviews modules
Add missing test coverage:
- reject-listing handler spec
- user-deactivated listener spec
- lead-score value object spec
- rating value object spec

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:38:45 +07:00
Ho Ngoc Hai
0593d40098 fix(lint): resolve all 24 ESLint errors across web, api and e2e
- Remove unused imports (waitFor, useAuthStore) in dashboard test files
- Convert import() type annotation to import type in comparison-store spec
- Add next-env.d.ts to ESLint ignores (auto-generated file)
- Fix empty object pattern in auth.fixture.ts
- Sort import order alphabetically in 5 API test files

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:42:00 +07:00
Ho Ngoc Hai
d824d16760 feat(security): add per-endpoint API rate limiting with Redis sliding window
Implement @EndpointRateLimit() decorator and EndpointRateLimitGuard for
granular per-endpoint rate limiting using a Redis sorted-set sliding window.
This prevents brute force attacks on auth endpoints, replay attacks on
payment callbacks, and scraping on search endpoints.

Applied rate limits:
- /auth/login: 5 req/min per IP
- /auth/register: 3 req/min per IP
- /listings POST: 10 req/min per user
- /search: 30 req/min per user
- /payments/callback/*: 100 req/min per IP

Features:
- True sliding window (sorted set) for accurate rate measurement
- Configurable key strategy (IP or authenticated user)
- Admin bypass support (enabled by default)
- Fail-open on Redis errors
- Proper 429 response with Retry-After header
- Rate limit headers (X-RateLimit-Limit/Remaining/Reset)
- 22 unit tests covering all scenarios

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:36:35 +07:00
Ho Ngoc Hai
62485fee98 feat(agents): add public agent profile page at /agents/[id]
Implements a public-facing agent profile page with:
- Backend: new GET /agents/:agentId/profile public API endpoint with
  agent info, active listings, quality score, and review stats
- Frontend: server-rendered profile page with generateMetadata for SEO,
  JSON-LD structured data (RealEstateAgent schema), breadcrumbs
- Agent profile displays bio, service areas, quality score gauge,
  active listing cards, reviews with star ratings, and contact CTA
- Mobile responsive layout with sticky contact sidebar on desktop
- Vietnamese UI text throughout, consistent with existing patterns

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:16:19 +07:00
Ho Ngoc Hai
37fab515b7 feat(web): add property comparison page with side-by-side view
Build a complete property comparison feature at /compare:
- Zustand store with localStorage persistence for selected listings (2-5)
- Side-by-side comparison table (price, area, price/m², amenities, location, etc.)
- Summary statistics banner (price range, area range, price/m² range)
- "Add to Compare" button on property cards and detail pages
- Floating comparison bar for quick access when listings are selected
- Bilingual i18n support (Vietnamese + English)
- 18 unit tests for store logic and comparison stats computation
- Mobile-responsive layout with horizontal scroll on comparison table

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:55:50 +07:00
Ho Ngoc Hai
55a01c5738 feat(web): centralise Vietnamese price formatting across all pages
Create a single `currency.ts` utility with `formatPrice`, `formatVND`,
`formatPricePerM2`, and `parseVND` to replace 9+ duplicated inline
formatters. This fixes inconsistent decimal handling (1.5M was truncated
to "1 triệu") and standardises price/m² display. Integrated across
property cards, listing detail, dashboard, analytics, payments, pricing,
and admin moderation pages with 19 new unit tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:33:31 +07:00
Ho Ngoc Hai
d4652fd0f9 fix(auth): use env-configurable bcrypt rounds to prevent test timeout
HashedPassword.vo.spec.ts was timing out because SALT_ROUNDS=12 is too
expensive for the test runner. Make bcrypt rounds configurable via
BCRYPT_ROUNDS env var (default 12 for production), and set BCRYPT_ROUNDS=4
in vitest config for fast unit tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:26:43 +07:00
Ho Ngoc Hai
6ebacbc9bf fix: apply consistent-type-imports across API codebase (728 lint errors)
- Convert `import type { X }` to `import { type X }` (inline-type-imports style)
- Suppress consistent-type-imports for `typeof import()` in instrument.ts
- Includes uncommitted agent work: metrics module, redis caching, audit logs,
  saved searches, circuit breaker, rate limiting, and admin enhancements

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:22:21 +07:00
Ho Ngoc Hai
68b65cb848 test(web): increase frontend test coverage to ~70% page coverage
- Fix vitest config to include [locale] directory tests (was excluded)
- Fix register.spec.tsx: use getByRole('heading') to avoid duplicate text match
- Fix search.spec.tsx: add QueryClientProvider wrapper and mock saved searches hook
- Add 12 new page test files covering dashboard, admin, public, and OAuth pages:
  - dashboard (main, profile, payments, subscription, KYC)
  - admin (dashboard, users)
  - public (landing, pricing)
  - analytics
  - OAuth callbacks (Google, Zalo)
- 29 test files, 174 tests, 16/23 pages covered (69.6%)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:14:16 +07:00
Ho Ngoc Hai
d62eb5f164 feat(web): add /pricing page with subscription tier comparison
Complete public pricing page showing all 4 subscription plans (FREE,
AGENT_PRO, INVESTOR, ENTERPRISE) with billing cycle toggle, feature
comparison table, VND formatting, and Vietnamese/English i18n support.
Also adds pricing link to public navigation header and footer.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 22:42:37 +07:00
Ho Ngoc Hai
1aad9b9f95 test: increase test coverage for listings, auth, and search modules
Add 33 new test files to reach coverage targets:
- Listings: 13 → 28 test files (50%+)
- Auth: 21 → 36 test files (50%+)
- Search: 10 → 13 test files (59%+)

New tests cover domain entities, value objects, services, guards,
decorators, DTOs, repositories, controllers, and event handlers.
Total: 204 test files, 1178 tests passing.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:39:20 +07:00
Ho Ngoc Hai
75a608031b fix: resolve lint errors in test files — group imports before vi.mock blocks
- local.strategy.spec.ts: move LocalStrategy import above vi.mock calls
- media-storage.service.spec.ts: move MinioMediaStorageService import above vi.mock calls
- Vitest hoists vi.mock regardless of source order, so grouping imports is safe

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:39:00 +07:00
Ho Ngoc Hai
cbd8fb6784 fix(shared): handle Prisma errors in GlobalExceptionFilter to return proper HTTP status codes
Prisma errors (P2025 record not found, P2002 unique constraint, P2003 foreign key)
were falling through to the catch-all handler and returning 500 Internal Server Error
instead of appropriate 404/409/400. This caused GET /listings/:id with a non-existent
ID to return 500 when the Prisma layer threw before the application null check.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:23:30 +07:00
Ho Ngoc Hai
d30c5630ce fix(lint): resolve restricted import and console.log warnings
Change circuit-breaker import in resilient-search.repository.ts to use
@modules/shared barrel export instead of deep path, fixing no-restricted-imports
error. Replace console.log with console.warn in encrypt-existing-kyc.ts script
to satisfy no-console rule.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:13:39 +07:00
Ho Ngoc Hai
9cfea31905 fix(auth): use custom UnauthorizedException for structured 401 error responses
LocalStrategy and auth controllers were importing UnauthorizedException
from @nestjs/common instead of @modules/shared. While both return 401,
only the custom DomainException-based version produces the structured
error format (errorCode, correlationId, timestamp) expected by the
GlobalExceptionFilter's primary code path.

Also adds handleRequest() override to LocalAuthGuard to ensure custom
exceptions from the strategy propagate directly without Passport
transforming them.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:07:54 +07:00
Ho Ngoc Hai
a003df9a8a fix(health): resolve 404 on /health endpoints — restructure routes under /health prefix
Root cause: HealthController used @Controller() (empty prefix) with @Get('health')
and @Get('ready') flat routes. The global prefix exclusion for 'health' and 'ready'
was unreliable for module-scoped controllers.

Changes:
- Set @Controller('health') prefix so routes are /health, /health/ready, /health/db, /health/redis
- Update global prefix exclusion to use 'health/(.*)' wildcard pattern
- Exclude health endpoints from CSRF middleware (K8s probes don't send cookies)
- Add dedicated /health/db and /health/redis endpoints per acceptance criteria
- Expand unit tests to cover all 4 health endpoints (15 tests passing)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:55:03 +07:00
Ho Ngoc Hai
d36a13d536 fix(reviews): resolve 404 on /reviews/* routes — type-only imports broke NestJS DI metadata
The ReviewsModule routes returned 404 because TypeScript `type` imports
(`import { type CommandBus }`) are erased at compile time, causing
`emitDecoratorMetadata` to emit `Function` instead of the actual class
reference. NestJS DI relies on `design:paramtypes` metadata to resolve
constructor dependencies; with `Function` as the token, it cannot match
providers and the module fails to initialize silently.

Changed all DI-injected classes (CommandBus, QueryBus, EventBus,
LoggerService, PrismaService) from `type` imports to value imports
across the reviews module. Added eslint-disable comments to suppress
the `consistent-type-imports` rule on those lines, since NestJS DI
fundamentally requires runtime class references.

Also added ReviewsController unit tests covering all 5 endpoints.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:44:36 +07:00
Ho Ngoc Hai
50c5168529 feat(web): add SEO optimization — JSON-LD, dynamic sitemap, meta tags for listings
Add comprehensive SEO support for property listing pages to improve
organic search visibility and social sharing.

Changes:
- Convert listing detail page from client-only to server component wrapper
  with generateMetadata() for per-listing title, description, OG tags,
  canonical URLs, and hreflang alternates
- Add JSON-LD structured data (Schema.org RealEstateListing) with price,
  location, property specs, and breadcrumb markup
- Add Website JSON-LD with SearchAction to root layout
- Upgrade sitemap.xml to dynamically include all active listings across
  both locales (vi, en) with ISR revalidation
- Improve robots.txt with pagination/sort exclusions and GPTBot block
- Create server-side fetch utility (listings-server.ts) for SSR data
- Extract client UI into ListingDetailClient component

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:38:28 +07:00
Ho Ngoc Hai
017d85247e fix(security): harden security headers across API and Web apps
- API: set X-Frame-Options to DENY via frameguard, add Permissions-Policy header, widen CSP connect-src for Swagger CDN
- Web: add HSTS header (1yr, includeSubDomains, preload), add payment=(self) to Permissions-Policy, make localhost:3001 in CSP connect-src dev-only

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:10:22 +07:00
Ho Ngoc Hai
bd33c92977 fix: resolve lint error and typecheck failures for MVP launch readiness
- Remove unused `registerUser` import in e2e/api/inquiries.spec.ts
- Add `override` modifier to class methods in query-provider.tsx

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 19:01:45 +07:00
Ho Ngoc Hai
411090875b feat(api): add per-type file size limits and 413 responses for media uploads
- FileValidationPipe now supports maxSizeByMimeType for per-MIME-type size limits
- Images: max 10MB, Video (MP4): max 100MB
- Oversized files return 413 Payload Too Large instead of 400 Bad Request
- MIME type validation runs before size check for clearer error messages
- Multer module limit raised to 100MB (per-type enforcement in pipe)
- Added 413 ApiResponse to Swagger docs on upload endpoint
- Added comprehensive unit tests for FileValidationPipe (16 test cases)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:18:01 +07:00
Ho Ngoc Hai
3418ab30b0 feat(mcp): add rate limiting and auth guard tests for MCP transport controller
MCP endpoints already had JwtAuthGuard applied but lacked per-route rate
limiting and test coverage for security behavior. Add @Throttle decorators
with appropriate limits (5 req/min for SSE connections, 30 req/min for
server list and messages), unit tests verifying guard/throttle metadata,
and E2E tests confirming 401 rejection for unauthenticated requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:12:19 +07:00
Ho Ngoc Hai
2432a20b45 feat(api): add async error handling to critical module handlers
Wrap async operations at application layer boundaries with proper
try/catch, LoggerService logging, and domain exceptions:
- UploadMediaHandler: mediaStorage.upload() error boundary
- ExportUserDataHandler: Promise.all() error logging
- ForceDeleteUserHandler: $transaction error logging
- LoginUserHandler: token generation error boundary
- RefreshTokenHandler: token rotation error boundary
- CreatePaymentHandler: payment gateway call error boundary

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:11:49 +07:00
Ho Ngoc Hai
4c432c7ff9 fix: resolve 21 lint errors from GDPR/logger/caching commits and fix web lint
- Fix import ordering in auth DTOs, admin module, and test files
- Merge duplicate @modules/shared imports (no-duplicates with prefer-inline)
- Remove unused imports (ForceDeleteUserCommand, Inject)
- Use parameterless catch for unused error bindings
- Switch web lint from `next lint` to `eslint` (flat config compatibility)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:00:37 +07:00
Ho Ngoc Hai
ab478a565a feat(web): add QueryErrorBoundary and use real map coordinates
Add global QueryErrorResetBoundary wrapping the app so TanStack Query
errors are caught with a retry UI instead of crashing. Enable
throwOnError in QueryClient defaults. Update ListingMap to use real
latitude/longitude from API when available, falling back to city-based
jitter for listings without coordinates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 17:58:35 +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
34202f2527 refactor(api): replace new Logger() with DI LoggerService and split large files
- Migrate 30 files from `new Logger(ClassName.name)` to injected LoggerService
  for consistent PII masking and centralized logging config
- Split prisma-admin-query.repository.ts (313→121 lines) into admin-stats.queries.ts
  and admin-user.queries.ts
- Split admin.controller.ts (285→154 lines) into admin-moderation.controller.ts
- Split prisma-listing.repository.ts (274→111 lines) into listing-read.queries.ts
- Update 28 test files with mock LoggerService
- All 831 tests passing, zero direct new Logger() calls remaining

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 05:35:04 +07:00
Ho Ngoc Hai
4e71036ddd feat(api): add listing search caching and apply @Cacheable decorator
- Add Redis caching to SearchListingsHandler (2 min TTL, query-based key)
- Refactor GetDistrictStatsHandler to use @Cacheable decorator
- Update search-listings test to provide mock CacheService

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 05:14:58 +07:00
Ho Ngoc Hai
372fae0d34 fix: remove unused CacheService import in cacheable decorator test
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 05:08:40 +07:00
Ho Ngoc Hai
2611cfa867 feat(api): add @Cacheable decorator and plan list caching
- Create @Cacheable method decorator for declarative cache-aside pattern
  with configurable prefix, TTL, resource label, and key extraction
- Add PLAN_LIST (1h TTL) and REFERENCE_DATA (24h TTL) cache constants
- Add CachePrefix.PLAN_LIST and CachePrefix.REFERENCE entries
- Cache subscription plan queries in GetPlanHandler (single + list)
- Export Cacheable decorator from shared module barrel
- Add comprehensive tests for decorator and handler caching

The caching infrastructure (CacheService, Redis, Prometheus metrics,
event-driven invalidation) was already production-ready with 10+ hot
paths cached. This commit adds the missing declarative decorator and
plan list caching.

Resolves: TEC-1567

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 10:26:59 +07:00
Ho Ngoc Hai
862078df37 feat(web): add auth+search i18n translations and filter-bar accessibility
Add missing auth and search translation namespaces to vi.json and en.json
that are required by login/register pages and search filter-bar component.
Update filter-bar with useTranslations('search'), aria-labels, and
role="search" for WCAG 2.1 AA compliance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 10:22:59 +07:00
Ho Ngoc Hai
8179f1c16e feat(api): complete domain event publishing with aggregate root pattern
- Add getUncommittedEvents() and commit() to AggregateRoot base class
- Create 6 new domain events: SubscriptionExpired, SubscriptionRenewed,
  ListingStatusChanged, UserKycUpdated, UserDeactivated, PaymentRefunded
- Wire events into entity state changes: SubscriptionEntity (markExpired,
  renewPeriod), ListingEntity (all transitions), UserEntity (KYC, deactivate),
  PaymentEntity (markRefunded)
- Add 7 new event listeners across notifications, admin, and search modules
  (25 total @OnEvent handlers)
- Fix ReviewDeletedListener to handle LISTING target type
- Restore watcher notifications in ListingSoldListener
- Update barrel exports and module registrations

Resolves: TEC-1564

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 10:22:20 +07:00
Ho Ngoc Hai
35feccb529 feat(analytics): integrate AI/ML services — AVM endpoint, moderation pipeline, market index cron
- Add AiServiceClient HTTP client for Python FastAPI AI service with timeout and fallback
- Add HttpAVMService that calls Python AVM endpoint, falls back to PrismaAVMService on failure
- Add ListingCreatedModerationHandler: auto-flags suspicious listings via AI moderation on create
- Add MarketIndexCronService: daily cron job aggregating market stats per district/city/type
- Wire ScheduleModule and new providers into AnalyticsModule and AppModule
- Add unit tests for AiServiceClient, HttpAVMService, and moderation handler (all passing)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 10:21:05 +07:00
Ho Ngoc Hai
d64bbe97e2 feat(api): add inquiries, leads, and agents modules for Agent Portal
Build three new DDD modules following existing CQRS patterns:
- Inquiries: CRUD endpoints for buyer consultation requests with agent notification support
- Leads: Full lead lifecycle management with status state machine and conversion tracking
- Agents: Quality score calculation (event-driven on review changes) and dashboard stats API

All modules include unit tests (14 test files, all 797 tests pass).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 10:01:16 +07:00
Ho Ngoc Hai
7195064f12 feat(web): add i18n locale routes and language switcher component
Add locale-prefixed routes for admin, auth, dashboard, and public pages.
Add error, loading, and not-found pages for locale context. Add language
switcher UI component for Vietnamese/English toggle.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:44:18 +07:00
Ho Ngoc Hai
2250e17a09 feat(api): add field encryption, health check specs, and KYC encryption script
- Add field-level encryption service for PII data with AES-256-GCM
- Add health check specs for Prisma and Redis indicators
- Add MCP controller specs
- Add encrypt-existing-kyc migration script for existing KYC data

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:44:00 +07:00
Ho Ngoc Hai
e927385ed5 feat(api): improve notifications, reviews, search, and subscriptions modules
- Add listing-sold event listener with spec for notifications
- Add review-deleted event listener with spec for reviews
- Improve search handlers with proper Typesense client injection
- Improve subscription handlers with ConfigService and quota tracking

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:43:39 +07:00
Ho Ngoc Hai
f15e98a33b feat(payments): improve VNPay, MoMo, ZaloPay services with ConfigService
Migrate payment gateway services from hardcoded config to NestJS
ConfigService injection. Improve payment handler error handling and
update gateway factory specs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:43:19 +07:00
Ho Ngoc Hai
c9fc1f52cb feat(listings): add price validator, moderation service, and improve handlers
Add domain-level price validator and moderation services with Prisma
implementation. Improve listing creation, status management, and media
upload handlers. Add price validator spec.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:43:06 +07:00