Files
goodgo-platform/apps/api/src/app.module.ts
Ho Ngoc Hai a9fa214544 feat: comprehensive seed, Lucide icons, grouped dashboard nav, API fixes
- Rewrite prisma/seed.ts to populate all 27 models with realistic
  Vietnamese real estate data (8 users with login, 10 properties,
  10 listings, orders, payments, reviews, notifications, etc.)
- Replace all emoji icons with Lucide React SVG icons across frontend
  for consistent rendering, sizing, and accessibility
- Redesign dashboard nav: grouped sidebar with section headers,
  primary/secondary split on desktop, icon-only secondary items
- Replace language switcher flag emoji with Globe icon
- Replace SVG theme toggle with Lucide Moon/Sun icons
- Fix API startup: graceful fallback for Sentry profiling, Google OAuth,
  and Zalo OAuth when credentials are not configured
- Relax rate limiting in development mode (10k req/min)
- Fix listings API to include media[] array in search response
- Add optional chaining for property.media across frontend components
- Update OAuth strategy tests to match graceful fallback behavior

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 11:13:04 +07:00

110 lines
3.8 KiB
TypeScript

import { type MiddlewareConsumer, Module, type NestModule, RequestMethod } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { CqrsModule } from '@nestjs/cqrs';
import { ScheduleModule } from '@nestjs/schedule';
import { ThrottlerModule } from '@nestjs/throttler';
import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
import { AdminModule } from '@modules/admin';
import { AgentsModule } from '@modules/agents';
import { AnalyticsModule } from '@modules/analytics';
import { AuthModule } from '@modules/auth';
import { HealthModule } from '@modules/health';
import { InquiriesModule } from '@modules/inquiries';
import { LeadsModule } from '@modules/leads';
import { ListingsModule } from '@modules/listings';
import { McpIntegrationModule } from '@modules/mcp';
import { HttpMetricsInterceptor, MetricsModule } from '@modules/metrics';
import { NotificationsModule } from '@modules/notifications';
import { PaymentsModule } from '@modules/payments';
import { ReviewsModule } from '@modules/reviews';
import { SearchModule } from '@modules/search';
import { SharedModule } from '@modules/shared';
import { ThrottlerBehindProxyGuard } from '@modules/shared/infrastructure/guards/throttler-behind-proxy.guard';
import { CsrfMiddleware } from '@modules/shared/infrastructure/middleware/csrf.middleware';
import { SanitizeInputMiddleware } from '@modules/shared/infrastructure/middleware/sanitize-input.middleware';
import { SubscriptionsModule } from '@modules/subscriptions';
import { AppController } from './app.controller';
@Module({
imports: [
SentryModule.forRoot(),
CqrsModule.forRoot(),
ScheduleModule.forRoot(),
SharedModule,
HealthModule,
AuthModule,
AgentsModule,
InquiriesModule,
LeadsModule,
ListingsModule,
ReviewsModule,
SearchModule,
NotificationsModule,
PaymentsModule,
SubscriptionsModule,
AdminModule,
AnalyticsModule,
MetricsModule,
McpIntegrationModule,
// ── Rate Limiting ──
// Default: 60 requests per 60 seconds per IP
// Override per-route with @Throttle() decorator
ThrottlerModule.forRoot({
throttlers: [
{
name: 'default',
ttl: 60_000,
limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 60,
},
{
name: 'auth',
ttl: 60_000,
limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 10,
},
{
name: 'payment-callback',
ttl: 60_000,
limit: process.env['NODE_ENV'] === 'test' || process.env['NODE_ENV'] === 'development' ? 10_000 : 20,
},
],
}),
],
controllers: [AppController],
providers: [
{
provide: APP_FILTER,
useClass: SentryGlobalFilter,
},
{
provide: APP_GUARD,
useClass: ThrottlerBehindProxyGuard,
},
{
provide: APP_INTERCEPTOR,
useClass: HttpMetricsInterceptor,
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
// Sanitize all incoming request strings to prevent stored XSS
consumer
.apply(SanitizeInputMiddleware)
.forRoutes('*');
// CSRF double-submit cookie (sets on GET, validates on state-changing methods)
// Exclude health endpoints — they must remain accessible without cookies
// Skip entirely in test mode so E2E / API tests can POST without a CSRF cookie
if (process.env['NODE_ENV'] !== 'test') {
consumer
.apply(CsrfMiddleware)
.exclude(
{ path: 'health', method: RequestMethod.GET },
{ path: 'health/(.*)', method: RequestMethod.GET },
)
.forRoutes('*');
}
}
}