generated from fahricansecer/boilerplate-be
225 lines
5.6 KiB
TypeScript
225 lines
5.6 KiB
TypeScript
import { Module, Logger as NestLogger } from '@nestjs/common';
|
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
|
|
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
|
|
import { CacheModule } from '@nestjs/cache-manager';
|
|
import { redisStore } from 'cache-manager-redis-yet';
|
|
import { LoggerModule } from 'nestjs-pino';
|
|
import {
|
|
I18nModule,
|
|
AcceptLanguageResolver,
|
|
HeaderResolver,
|
|
QueryResolver,
|
|
} from 'nestjs-i18n';
|
|
import * as path from 'path';
|
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
|
|
// Config
|
|
import {
|
|
appConfig,
|
|
databaseConfig,
|
|
jwtConfig,
|
|
redisConfig,
|
|
i18nConfig,
|
|
featuresConfig,
|
|
throttleConfig,
|
|
} from './config/configuration';
|
|
import { geminiConfig } from './modules/gemini/gemini.config';
|
|
import { validateEnv } from './config/env.validation';
|
|
|
|
// Common
|
|
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
|
|
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
|
|
|
// Database
|
|
import { DatabaseModule } from './database/database.module';
|
|
|
|
// Modules
|
|
import { AuthModule } from './modules/auth/auth.module';
|
|
import { UsersModule } from './modules/users/users.module';
|
|
import { AdminModule } from './modules/admin/admin.module';
|
|
import { HealthModule } from './modules/health/health.module';
|
|
import { GeminiModule } from './modules/gemini/gemini.module';
|
|
import { CmsModule } from './modules/cms/cms.module';
|
|
import { MailModule } from './modules/mail/mail.module';
|
|
|
|
// Guards
|
|
import {
|
|
JwtAuthGuard,
|
|
RolesGuard,
|
|
PermissionsGuard,
|
|
} from './modules/auth/guards';
|
|
|
|
@Module({
|
|
imports: [
|
|
// Configuration
|
|
ConfigModule.forRoot({
|
|
isGlobal: true,
|
|
validate: validateEnv,
|
|
load: [
|
|
appConfig,
|
|
databaseConfig,
|
|
jwtConfig,
|
|
redisConfig,
|
|
i18nConfig,
|
|
featuresConfig,
|
|
throttleConfig,
|
|
geminiConfig,
|
|
],
|
|
}),
|
|
|
|
// Logger (Structured Logging with Pino)
|
|
LoggerModule.forRootAsync({
|
|
imports: [ConfigModule],
|
|
inject: [ConfigService],
|
|
useFactory: (configService: ConfigService) => {
|
|
return {
|
|
pinoHttp: {
|
|
level: configService.get('app.isDevelopment') ? 'debug' : 'info',
|
|
transport: configService.get('app.isDevelopment')
|
|
? {
|
|
target: 'pino-pretty',
|
|
options: {
|
|
singleLine: true,
|
|
},
|
|
}
|
|
: undefined,
|
|
},
|
|
};
|
|
},
|
|
}),
|
|
|
|
// i18n
|
|
I18nModule.forRootAsync({
|
|
useFactory: (configService: ConfigService) => ({
|
|
fallbackLanguage: configService.get('i18n.fallbackLanguage', 'en'),
|
|
loaderOptions: {
|
|
path: path.join(__dirname, '/i18n/'),
|
|
watch: configService.get('app.isDevelopment', true),
|
|
},
|
|
}),
|
|
resolvers: [
|
|
new HeaderResolver(['x-lang', 'accept-language']),
|
|
new QueryResolver(['lang']),
|
|
AcceptLanguageResolver,
|
|
],
|
|
inject: [ConfigService],
|
|
}),
|
|
|
|
// Throttling
|
|
ThrottlerModule.forRootAsync({
|
|
inject: [ConfigService],
|
|
useFactory: (configService: ConfigService) => [
|
|
{
|
|
ttl: configService.get('throttle.ttl', 60000),
|
|
limit: configService.get('throttle.limit', 100),
|
|
},
|
|
],
|
|
}),
|
|
|
|
// Caching (Redis with in-memory fallback)
|
|
CacheModule.registerAsync({
|
|
isGlobal: true,
|
|
imports: [ConfigModule],
|
|
useFactory: async (configService: ConfigService) => {
|
|
const logger = new NestLogger('CacheModule');
|
|
const useRedis = configService.get('REDIS_ENABLED', 'false') === 'true';
|
|
|
|
if (useRedis) {
|
|
try {
|
|
const store = await redisStore({
|
|
socket: {
|
|
host: configService.get('redis.host', 'localhost'),
|
|
port: configService.get('redis.port', 6379),
|
|
},
|
|
ttl: 60 * 1000, // 1 minute default
|
|
});
|
|
logger.log('✅ Redis cache connected');
|
|
return {
|
|
store: store as unknown as any,
|
|
ttl: 60 * 1000,
|
|
};
|
|
} catch {
|
|
logger.warn('⚠️ Redis connection failed, using in-memory cache');
|
|
}
|
|
}
|
|
|
|
// Fallback to in-memory cache
|
|
logger.log('📦 Using in-memory cache');
|
|
return {
|
|
ttl: 60 * 1000,
|
|
};
|
|
},
|
|
inject: [ConfigService],
|
|
}),
|
|
|
|
// Database
|
|
DatabaseModule,
|
|
|
|
// Core Modules
|
|
AuthModule,
|
|
UsersModule,
|
|
AdminModule,
|
|
|
|
// Optional Modules (controlled by env variables)
|
|
GeminiModule,
|
|
HealthModule,
|
|
|
|
// CMS Module
|
|
CmsModule,
|
|
|
|
// Mail Module
|
|
MailModule,
|
|
|
|
// Serve uploaded files
|
|
ServeStaticModule.forRoot({
|
|
rootPath: path.join(__dirname, '..', 'uploads'),
|
|
serveRoot: '/uploads',
|
|
}),
|
|
],
|
|
providers: [
|
|
// Global Exception Filter
|
|
{
|
|
provide: APP_FILTER,
|
|
useClass: GlobalExceptionFilter,
|
|
},
|
|
|
|
// Global Response Interceptor
|
|
{
|
|
provide: APP_INTERCEPTOR,
|
|
useClass: ResponseInterceptor,
|
|
},
|
|
|
|
// Global Rate Limiting
|
|
{
|
|
provide: APP_GUARD,
|
|
useClass: ThrottlerGuard,
|
|
},
|
|
|
|
// Global JWT Auth Guard
|
|
{
|
|
provide: APP_GUARD,
|
|
useClass: JwtAuthGuard,
|
|
},
|
|
|
|
// Global Roles Guard
|
|
{
|
|
provide: APP_GUARD,
|
|
useClass: RolesGuard,
|
|
},
|
|
|
|
// Global Permissions Guard
|
|
{
|
|
provide: APP_GUARD,
|
|
useClass: PermissionsGuard,
|
|
},
|
|
],
|
|
})
|
|
export class AppModule {
|
|
private readonly logger = new NestLogger(AppModule.name);
|
|
|
|
constructor() {
|
|
this.logger.log('AppModule initialized');
|
|
}
|
|
}
|