Commit Graph

189 Commits

Author SHA1 Message Date
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
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
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
8265130477 docs: update changelog, project tracker, QA tracker, and implementation plan
Refresh project documentation to reflect current state of the platform
including recent features, test improvements, and QA status.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:38:30 +07:00
Ho Ngoc Hai
642b593884 docs: move remaining analysis docs to docs/audits/
Move MCP module exploration, quick reference, and inquiries exploration
documents to the centralized audit directory.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:38:14 +07:00
Ho Ngoc Hai
b8512ebff4 docs: consolidate audit and analysis reports into docs/audits/
Move 36 root-level audit/analysis documents and 7 web app audit documents
into docs/audits/ directory to declutter the project root. Remove stale
EXPLORATION_SUMMARY.txt.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:37:50 +07:00
Ho Ngoc Hai
64c6074735 feat(devops): add staging auto-deploy pipeline on develop branch
- Trigger deploy workflow on push to `develop` branch (in addition to `master`)
- Add `staging-latest` Docker image tag for develop branch builds
- Add `rollback-staging` job: auto-reverts to previous images on smoke test failure
- Add Slack success notification for staging deploys (previously only failure was notified)
- Record pre-deploy image digests for rollback capability
- Update deployment docs with CI/CD pipeline details, rollback procedures, and required secrets

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:18:37 +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
f27b13f712 docs: add production operational runbook
Create comprehensive docs/RUNBOOK.md covering all 14 production services,
health checks, 10 common incident scenarios with diagnosis/resolution,
recovery procedures (DB restore, Redis flush, rolling restart, rollback),
escalation matrix, monitoring dashboards, and PromQL queries.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:28:02 +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
b7f9664709 feat(devops): add one-command bootstrap dev setup script
Streamline developer onboarding with a single bootstrap.sh that checks
prerequisites, configures .env with generated secrets, installs deps,
starts Docker services, and runs migrations + seeding.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 00:18:36 +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
18b5980f29 docs: consolidate and update project documentation
- Fix port numbers across all docs (API :3001, Web :3000)
- Add 6 missing modules to README, CLAUDE.md, and architecture doc
  (agents, health, inquiries, leads, reviews, metrics/web-vitals)
- Add Swagger UI reference and /api/v1 prefix notes
- Create docs/api-endpoints.md with complete REST API reference
- Create docs/README.md as documentation index
- Update deployment guide with Loki, Promtail, pg-backup services
- Update health check table with all current endpoints

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:32:00 +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
8cdfe17205 feat(ops): add automated backup restore verification
Adds pg-verify-backup.sh that restores the latest backup to an isolated
test database and verifies integrity (table existence, row counts, key
checksums, PostGIS extension, indexes, enum types). Reports pass/fail
with optional JSON output.

- Cron schedule: daily at 04:00 UTC (2h after backup)
- On-demand: docker compose run --rm pg-verify-backup
- CI: weekly GitHub Actions workflow with artifact upload

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:19:43 +07:00
Ho Ngoc Hai
90839cf542 feat(monitoring): add API latency Grafana dashboard and alerting rules
Create comprehensive Grafana dashboard for API latency monitoring with:
- p50/p95/p99 stat panels and time series for all endpoints
- Per-endpoint latency breakdown with route/method template variables
- Top 10 slowest endpoints table and bar chart (by p99)
- Request rate (by method) and error rate (4xx/5xx) panels
- Error rate percentage (5xx/total) with SLO threshold
- Latency heatmap and histogram distribution panels

Add Prometheus alerting rules:
- ApiLatencyP99High: p99 > 1s for 5m (warning)
- ApiEndpointLatencyP99High: per-endpoint p99 > 2s (warning)
- ApiLatencyP99Critical: p99 > 3s for 3m (critical/SLO breach)
- ApiErrorRate5xxHigh: 5xx rate > 1% for 5m (warning)

Fix api-overview.json using wrong metric name
(http_request_duration_seconds → goodgo_api_request_duration_seconds).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:18:09 +07:00
Ho Ngoc Hai
59272e9321 chore(docs): consolidate 22 audit files from root into docs/audits/
Root directory had accumulated audit/exploration markdown files cluttering
the project root. Moved all audit-related files to docs/audits/ with a
README.md index, and updated cross-references in K6_LOAD_TESTING_GUIDE.md
and README_FRONTEND_DOCS.md.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 23:16:00 +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
783b5b74f1 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:21:25 +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
9b786c1c95 deps: enhance Dependabot config for monorepo coverage and security
- Add npm monitoring for apps/api, apps/web, and libs/mcp-servers
  directories alongside root workspace
- Reduce open-pull-requests-limit from 10 to 5 per ecosystem
- Add dependency groups for Next.js and React packages
- Remove stale pip and docker entries for non-existent libs/ai-services
- Add documentation header explaining security update strategy
- Security updates rely on GitHub's built-in Dependabot Security
  Updates feature (daily automatic PRs for advisories)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:13:09 +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
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
f5ef9d8c86 docs: add comprehensive API error codes reference for frontend consumption
Document all 33 structured errorCode values from DomainException/ErrorCode
enum across all modules (auth, user, listing, property, media, payment,
subscription, course). Includes HTTP status mapping, Vietnamese error
messages, usage examples per module, alphabetical quick-reference table,
and TypeScript integration guide for frontend error handling.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 20:11:12 +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
2a8799ac5b fix(ci): correct workflow branch targets from main to master
All three GitHub Actions workflows (CI, E2E, Deploy) referenced
branches: [main] but the repository default branch is master.
This meant CI never triggered on pushes or PRs to master.

- ci.yml: push/PR triggers → master
- e2e.yml: push/PR triggers → master
- deploy.yml: push trigger + latest tag condition → master

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 19:20:49 +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
08d84a56a3 docs: update PROJECT_TRACKER.md — all 51 tasks complete across 6 phases
Synced tracker with Paperclip issue statuses. All Phase 4-6 tasks
(security hardening, quality, feature completion) confirmed done.
Platform is MVP-ready for launch review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:31:56 +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
eaa4925653 feat(e2e): add payment fixtures for VNPay and MoMo callback testing
Add buildVnpayCallbackData and buildMomoCallbackData fixture helpers
that generate valid HMAC signatures for E2E payment callback tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 05:10:10 +07:00