import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { APP_FILTER } from '@nestjs/core'; import { LoggerModule } from 'nestjs-pino'; import { AdminModule } from './admin/admin.module'; import { AuditModule } from './audit/audit.module'; import { AuthModule } from './auth/auth.module'; import { BackupModule } from './backup/backup.module'; import { ApiExceptionFilter } from './common/api-exception.filter'; import { maskTokenParam } from './common/mask-token-param'; import { SecurityHeadersMiddleware } from './common/security-headers.middleware'; import { CommentsModule } from './comments/comments.module'; import { CompactionModule } from './compaction/compaction.module'; import { AppConfig } from './config/app-config.service'; import { ConfigModule } from './config/config.module'; import { FilesModule } from './files/files.module'; import { GrantsModule } from './grants/grants.module'; import { HealthModule } from './health/health.module'; import { HomeModule } from './home/home.module'; import { ImportExportModule } from './import-export/import-export.module'; import { LabelsModule } from './labels/labels.module'; import { LegalModule } from './legal/legal.module'; import { LinksModule } from './links/links.module'; import { MailModule } from './mail/mail.module'; import { McpModule } from './mcp/mcp.module'; import { MembersModule } from './members/members.module'; import { PagesModule } from './pages/pages.module'; import { PermissionsModule } from './permissions/permissions.module'; import { PluginsModule } from './plugins/plugins.module'; import { PondsModule } from './ponds/ponds.module'; import { PrismaModule } from './prisma/prisma.module'; import { PublicApiModule } from './public-api/public-api.module'; import { PublicModule } from './public/public.module'; import { RateLimitModule } from './rate-limit/rate-limit.module'; import { ReadTrailModule } from './read-trail/read-trail.module'; import { SearchModule } from './search/search.module'; import { SettingsModule } from './settings/settings.module'; import { SetupModule } from './setup/setup.module'; import { TrashModule } from './trash/trash.module'; import { UsersModule } from './users/users.module'; import { NotificationsModule } from './notifications/notifications.module'; import { WatchesModule } from './watches/watches.module'; import { FavoritesModule } from './favorites/favorites.module'; import { VersionsModule } from './versions/versions.module'; @Module({ imports: [ ConfigModule, PrismaModule, AuditModule, ReadTrailModule, RateLimitModule, MailModule, SettingsModule, // Before SetupModule and AuthModule: global guards run in registration // order, and the maintenance gate (in-app restore, issue #103) must // answer before anything touches the mid-restore database. BackupModule, // Before AuthModule: the setup gate must win over AuthGuard's 401 while // setup is pending. SetupModule, UsersModule, PermissionsModule, PondsModule, PagesModule, CommentsModule, WatchesModule, FavoritesModule, NotificationsModule, FilesModule, TrashModule, CompactionModule, VersionsModule, LabelsModule, LegalModule, HomeModule, LinksModule, SearchModule, GrantsModule, MembersModule, PublicModule, PublicApiModule, McpModule, ImportExportModule, PluginsModule, AuthModule, AdminModule, LoggerModule.forRootAsync({ inject: [AppConfig], useFactory: (config: AppConfig) => ({ pinoHttp: { level: config.env.LOG_LEVEL, // Human-readable logs in local development, JSON everywhere else. transport: config.env.NODE_ENV === 'development' ? { target: 'pino-pretty' } : undefined, autoLogging: config.env.NODE_ENV !== 'test', // Request bodies are never logged (operations.md logging rules). redact: { paths: ['req.headers.authorization', 'req.headers.cookie'], remove: true }, // Feed tokens travel as `?token=` (issue #191) — mask them so the // request log never stores the credential. serializers: { req: (req: { url?: string }) => ({ ...req, url: maskTokenParam(req.url) }), }, }, }), }), HealthModule, ], providers: [{ provide: APP_FILTER, useClass: ApiExceptionFilter }], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer): void { // Module-level (not main.ts) so createTestApp boots the identical // security-header/CORS middleware — see security-headers.middleware.ts. consumer.apply(SecurityHeadersMiddleware).forRoutes('{*path}'); } }