API bootstrap fixes (DI wiring):
- analytics.module: add forwardRef(() => AdminModule) to import
AI_CONFIG_PROVIDER for GetListingAiAdviceHandler + GetProjectAiAdviceHandler
- listings.module: add PaymentsModule to imports so PAYMENT_INITIATOR is
resolvable by FeatureListingHandler
- metrics.module: register 3 missing Prometheus providers that MetricsService
injects (READ_MODEL_PROJECTOR_LAG_SECONDS / REFRESH_DURATION /
RECONCILIATION_DRIFT_TOTAL) — caused boot failure previously
- get-listing-ai-advice.handler: switch LISTING_REPOSITORY import from barrel
@modules/listings to direct internal path to break circular reference that
made the symbol evaluate as undefined at decorator time
- shared.module: comment out broken EVENT_BUS / OutboxService / OutboxRelay
providers (depend on @goodgo/contracts-events workspace pkg not yet wired)
CSRF middleware:
- Rewrite exclude logic as inline path-check inside the middleware itself.
Nest 11 + path-to-regexp v8 changed how MiddlewareConsumer.exclude() matches
against forRoutes('*') — the previous string patterns silently stopped
matching, causing every POST to /auth/login to return 403 CSRF Forbidden.
Inlined exempt list strips the /api/v1 prefix and checks against a Set.
Admin revenue stats:
- admin-stats.queries: use Prisma.sql template fragments for DATE_TRUNC unit
('day'|'month'). Passing the unit as a bind parameter caused Postgres error
42803 (column must appear in GROUP BY) because the planner treats $1 as an
opaque scalar and cannot prove SELECT and GROUP BY expressions are equal.
Admin audit-log page:
- SeverityPill: add ?? 'info' fallback — backend AuditLogEntry does not
include a `severity` field, so SEVERITY_CONFIG[undefined] was undefined
and .dir threw TypeError, crashing the whole audit-log page.
DB seed fixes:
- seed.ts: replace Vietnamese enum literals ('Sổ hồng', 'Sổ đỏ') with
correct enum keys ('SO_HONG', 'SO_DO') for the LegalStatus column
- seed-industrial-parks.ts: gate the standalone main() behind
require.main === module so importing the file from seed.ts doesn't
immediately close the pg.Pool used by the orchestrator
- scripts/seed-industrial-listings.ts: restore from tmp/ stash; was missing
from scripts/ causing seed.ts import to fail at startup
- migration 20260429010000_add_property_certificate_verified: Property table
was missing the certificateVerified column required by seed + Prisma schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add three new NestJS modules following DDD/CQRS architecture:
- Industrial: KCN (industrial park) management with PostGIS geo queries, Typesense search, and market statistics
- Transfer: Furniture/premises transfer listings with AI-powered price estimation and depreciation modeling
- Reports: Async AI report generation via BullMQ with Claude narrative service, PDF generation, and macro data integration
Includes Prisma schema models, migrations, seed scripts, and app.module wiring with BullMQ Redis config.
Co-Authored-By: Paperclip <noreply@paperclip.ing>