// Preload .env before any module imports import * as dotenv from 'dotenv'; dotenv.config({ path: ['.env', '.env.local'] }); import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler'; import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'; import { appConfig } from './config/app.config'; import { databaseConfig } from './config/database.config'; import { redisConfig } from './config/redis.config'; import { rateLimitConfig } from './config/rate-limit.config'; import { JwtAuthGuard } from './common/guards/jwt-auth.guard'; import { TenantsModule } from './modules/tenants/tenants.module'; import { AuthModule } from './modules/auth/auth.module'; import { TgAccountsModule } from './modules/tg-accounts/tg-accounts.module'; import { TgAuthModule } from './modules/tg-auth/tg-auth.module'; import { TgProxiesModule } from './modules/tg-proxies/tg-proxies.module'; import { TdlibModule } from './modules/tdlib/tdlib.module'; import { TgChatsModule } from './modules/tg-chats/tg-chats.module'; import { TgDialogsModule } from './modules/tg-dialogs/tg-dialogs.module'; import { TgContactsModule } from './modules/tg-contacts/tg-contacts.module'; import { TgMessagesModule } from './modules/tg-messages/tg-messages.module'; import { TgGroupsModule } from './modules/tg-groups/tg-groups.module'; import { TgChannelsModule } from './modules/tg-channels/tg-channels.module'; import { TgFilesModule } from './modules/tg-files/tg-files.module'; import { TgWebhooksModule } from './modules/tg-webhooks/tg-webhooks.module'; import { TgRateLimitModule } from './modules/tg-rate-limit/tg-rate-limit.module'; import { TgAuditModule } from './modules/tg-audit/tg-audit.module'; import { AdminModule } from './modules/admin/admin.module'; import { TgUpdatesModule } from './modules/tg-updates/tg-updates.module'; import { AuditLogInterceptor } from './common/interceptors/audit-log.interceptor'; // Conditionally import BullMQ const redisEnabled = process.env.REDIS_ENABLED !== 'false'; const bullImports: any[] = []; const bullProviders: any[] = []; if (redisEnabled) { const { BullModule } = require('@nestjs/bull'); bullImports.push( BullModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ redis: { host: config.get('redis.host'), port: config.get('redis.port'), password: config.get('redis.password'), db: config.get('redis.db'), }, }), }), ); } else { // Provide mock Bull queue tokens so injectors don't crash const { getQueueToken } = require('@nestjs/bull'); const queueNames = ['send-message', 'file-download', 'webhook-delivery']; for (const name of queueNames) { bullProviders.push({ provide: getQueueToken(name), useValue: { add: () => Promise.reject(new Error('Redis/BullMQ not available')), process: () => {} }, }); bullProviders.push({ provide: 'BullQueue_' + name, useValue: { add: () => Promise.reject(new Error('Redis/BullMQ not available')), process: () => {} }, }); } } const imports: any[] = [ ConfigModule.forRoot({ isGlobal: true, load: [appConfig, databaseConfig, redisConfig, rateLimitConfig], envFilePath: ['.env'], }), TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => { const dbType = config.get('database.type') || 'postgres'; if (dbType === 'sqlite') { const sqlitePath = config.get('database.sqlitePath') || 'data/gateway.sqlite'; const inMemory = sqlitePath === ':memory:'; return { type: 'sqljs', ...(inMemory ? {} : { location: sqlitePath }), autoSave: !inMemory, entities: [__dirname + '/database/entities/**/*.entity.{ts,js}'], synchronize: true, }; } return { type: 'postgres', host: config.get('database.host'), port: config.get('database.port'), username: config.get('database.username'), password: config.get('database.password'), database: config.get('database.database'), entities: [__dirname + '/database/entities/**/*.entity.{ts,js}'], synchronize: config.get('database.synchronize') || config.get('app.nodeEnv') === 'development', migrationsRun: config.get('database.migrationsRun') || false, migrations: [__dirname + '/database/migrations/*.{ts,js}'], logging: config.get('app.nodeEnv') === 'development', }; }, }), ...bullImports, ThrottlerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ throttlers: [ { ttl: config.get('rateLimit.defaultTtl') || 60000, limit: config.get('rateLimit.defaultLimit') || 30, }, ], }), }), TenantsModule, AuthModule, TgAccountsModule, TgAuthModule, TgProxiesModule, TdlibModule, TgChatsModule, TgDialogsModule, TgContactsModule, TgMessagesModule, TgGroupsModule, TgChannelsModule, TgFilesModule, TgWebhooksModule, TgRateLimitModule, TgAuditModule, AdminModule, TgUpdatesModule, ]; @Module({ imports, providers: [ { provide: APP_GUARD, useClass: JwtAuthGuard, }, { provide: APP_GUARD, useClass: ThrottlerGuard, }, { provide: APP_INTERCEPTOR, useClass: AuditLogInterceptor, }, ...bullProviders, ], }) export class AppModule {}