- 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>
- 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>
- 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>
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>
- 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>
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>
- 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>
Add event listener for user-banned events with spec. Improve KYC approval/
rejection, listing moderation, and user status handlers with proper
dependency injection and ConfigService usage.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add unit tests for get-profile, get-agent-by-user-id, and verify-kyc handlers.
Improve OAuth service, local strategy, and repository implementations with
proper ConfigService injection and error handling.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add property valuation query handler with AVM (Automated Valuation Model)
service integration. Improve market index, heatmap, and price trend handlers
with proper dependency injection and error handling.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add Swagger decorators (@ApiTags, @ApiOperation, @ApiResponse, @ApiParam,
@ApiBearerAuth) to MCP transport controller — the only controller missing them
- Add reviews and mcp tags to DocumentBuilder config
- Enable JSON spec export at /api/v1/docs-json
- Update Helmet CSP to allow Swagger UI assets from cdn.jsdelivr.net
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The i18n architecture (config, routing, translation files, locale pages) was
already built but non-functional due to three missing pieces:
1. next-intl not listed in package.json
2. middleware.ts not using createMiddleware from next-intl/middleware
3. next.config.js not wrapped with createNextIntlPlugin
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Enable prefer-inline for import-x/no-duplicates to support barrel
import patterns (value + type imports from same module)
- Inline duplicate type imports in middleware.ts and listing-form-steps.tsx
- Fix import ordering across API test files and MCP controller
- Add next-intl mock to search spec (FilterBar uses useTranslations)
- Exclude [locale] test duplicates from vitest (need proper i18n test setup)
All 801 tests passing (653 API + 119 web + 29 MCP). Zero lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
The env-validation module previously only checked that JWT_SECRET and
JWT_REFRESH_SECRET were _present_ — it accepted any value, including
known placeholders like "CHANGE_ME". This meant a developer could copy
.env.example verbatim and run the app with predictable, forgeable tokens.
Changes:
- Add FORBIDDEN_SECRET_VALUES blocklist (case-insensitive) with 23 common
placeholder strings (CHANGE_ME, secret, password, test, etc.)
- Enforce minimum 32-character length for JWT secrets (NIST HMAC guidance)
- Export validateJwtSecret() for direct testing and reuse
- Update .env.example: replace "CHANGE_ME" with generation instructions
- Add 14 unit tests covering placeholder rejection, length enforcement,
missing-var errors, and production-mode validation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add 1-min TTL caching to CheckQuotaHandler (previously uncached, hitting
3 DB queries per guarded request). Add cache invalidation to
MeterUsageHandler and UpgradeSubscriptionHandler so quota caches stay
fresh after usage metering and plan changes. Increase search results TTL
from 1min to 2min per spec. Add market cache invalidation on listing
creation to keep district stats and market reports consistent.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add HealthModule with /health (liveness) and /ready (readiness) probes.
Readiness checks DB (Prisma) and Redis connectivity.
Replaces the basic /health endpoint in AppController.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
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>
Implement polymorphic reviews system supporting any target type (agent,
property, etc.) with DDD/CQRS architecture following existing patterns.
Endpoints:
- POST /api/reviews — create review (authenticated)
- GET /api/reviews?targetType=&targetId= — list reviews by target
- GET /api/reviews/stats?targetType=&targetId= — aggregate rating stats
- GET /api/reviews/me — list authenticated user's reviews
- DELETE /api/reviews/:id — delete own review
Business rules: 1-5 rating validation, self-review prevention, one
review per user per target. Includes 15 unit tests for all handlers.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
New test coverage for infrastructure and presentation layers across
multiple modules including Momo/ZaloPay payment services, Typesense
search repository, listing indexer, and notification handlers.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Update cache service with better error handling and analytics
query handlers to use consistent caching patterns.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Separate root route from health check endpoint. The /health endpoint
now returns timestamp for monitoring integration.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Remove hardcoded minioadmin/minioadmin_secret fallback from docker-compose.yml,
require MINIO_ACCESS_KEY/MINIO_SECRET_KEY env vars (fail-fast with :? syntax)
- Align docker-compose.yml env var names with .env.example (MINIO_ACCESS_KEY/SECRET_KEY)
- Update CI e2e workflow to use GitHub vars with non-default fallbacks
- Update .env.test to use non-default test credentials
- Add @aws-sdk/s3-request-presigner and getPresignedUploadUrl() method to
MinioMediaStorageService for properly signed client-side uploads
- Remove hardcoded credentials from dev-environment docs
Co-Authored-By: Paperclip <noreply@paperclip.ing>
validateEnv() previously skipped validation entirely when NODE_ENV !== 'production',
allowing the app to start without JWT_SECRET and JWT_REFRESH_SECRET in dev/staging.
Split required vars into ALWAYS_REQUIRED (JWT secrets) and REQUIRED_IN_PRODUCTION
(infrastructure) so security-critical secrets are validated in every environment.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Extract metric names into constants with goodgo_ prefix for business metrics
- Add MetricsService for type-safe metric recording
- Add HttpMetricsInterceptor for automatic request duration/count tracking
- Register interceptor globally via APP_INTERCEPTOR
- Include linter auto-fixes for test files
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add DuplicateDetector domain service that flags potential duplicate listings
using PostGIS ST_DWithin geo-proximity (100m radius) combined with trigram-based
title similarity (>70% threshold). Detection runs during CreateListing but never
blocks creation — warnings are returned in the response for seller/admin review.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- 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>
Add complete OAuth2 authentication flow for Google and Zalo providers:
- OAuthService: handles account linking (by email/phone), new user
creation for OAuth-only accounts, and JWT token generation
- GoogleOAuthStrategy: passport-google-oauth20 integration
- ZaloOAuthStrategy: custom OAuth2 implementation using Zalo's API
(authorization URL generation, code exchange, user info fetch)
- OAuthController: redirect and callback endpoints for both providers
with httpOnly cookie-based token management
- Unit tests for OAuthService (7 tests), GoogleOAuthStrategy (4 tests),
and ZaloOAuthStrategy (7 tests)
- OAuth env vars added to .env.example and env-validation warnings
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add resource limits (memory/CPU) and reservations for all services
- Add security hardening: read_only, no-new-privileges, tmpfs for temp dirs
- Add missing prod services: loki, promtail, pg-backup from dev compose
- Fix API healthcheck to include catch() for proper exit codes
- Add json-file logging driver with rotation limits across all services
- Remove exposed PostgreSQL port in prod (internal only)
- Add shm_size for PostgreSQL shared memory
- Add non-root user (appuser) to AI services Dockerfile
- Add --chown=node:node to COPY directives in API/Web Dockerfiles
- Harden .dockerignore: exclude IDE files, OS files, docker-compose files
- Fix Redis URL to include password authentication
- Add JWT_REFRESH_SECRET to API environment
- Add Grafana dependency on Loki for log datasource
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Register SanitizeInputMiddleware for all routes to prevent stored XSS
- Register CsrfMiddleware for all routes (sets cookie on GET, validates on state-changing methods)
- Remove unsafe-inline from CSP scriptSrc directive
- AppModule now implements NestModule with configure() method
Co-Authored-By: Paperclip <noreply@paperclip.ing>