This commit is contained in:
2026-03-09 11:38:43 +03:30
commit 5fda2d6d8c
339 changed files with 186841 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
import { Keyv } from 'keyv';
import KeyvRedis from '@keyv/redis';
import type { CacheModuleAsyncOptions } from '@nestjs/cache-manager';
import { ConfigService } from '@nestjs/config';
import { CacheableMemory } from 'cacheable';
export function cacheConfig(): CacheModuleAsyncOptions {
return {
isGlobal: true,
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const redisUri = configService.get('REDIS_URI');
const namespace = configService.get('CACHE_NAMESPACE', 'app-cache');
const ttl = +configService.get<number>('CACHE_TTL', 3600); //1 hour
return {
stores: [
new Keyv({
store: new CacheableMemory({ ttl: ttl * 1000, lruSize: 5000 }),
namespace: namespace
}),
new KeyvRedis(redisUri, { namespace: namespace }),
],
};
},
};
}
+16
View File
@@ -0,0 +1,16 @@
import type { HttpModuleAsyncOptions } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
export function httpConfig(): HttpModuleAsyncOptions {
return {
inject: [ConfigService],
global: true,
useFactory: (configService: ConfigService) => {
return {
timeout: configService.getOrThrow<number>('AXIOS_TIMEOUT'),
headers: { 'Content-Type': 'application/json' },
global: true,
};
},
};
}
+101
View File
@@ -0,0 +1,101 @@
import { defineConfig, PostgreSqlDriver } from '@mikro-orm/postgresql';
import * as dotenv from 'dotenv';
// 1. Load environment variables from your .env file
dotenv.config();
// 2. Read variables directly from process.env
const DB_PASS = process.env.DB_PASS;
const DB_USER = process.env.DB_USER;
const DB_HOST = process.env.DB_HOST;
const DB_PORT = process.env.DB_PORT ? +process.env.DB_PORT : 5432;
const DB_NAME = process.env.DB_NAME;
const isProduction = process.env.NODE_ENV === 'production';
// 3. Add checks to ensure variables are loaded
if (!DB_PASS || !DB_USER || !DB_HOST || !DB_PORT || !DB_NAME) {
throw new Error('One or more database environment variables are not set.');
}
const encodedPassword = encodeURIComponent(DB_PASS);
// 4. Export the result of defineConfig as the default
export default defineConfig({
entities: ['dist/**/*.entity.js'],
entitiesTs: ['src/**/*.entity.ts'],
driver: PostgreSqlDriver,
dbName: DB_NAME,
debug: false,
clientUrl: `postgres://${DB_USER}:${encodedPassword}@${DB_HOST}:${DB_PORT}`,
ensureDatabase: isProduction
? false
: {
forceCheck: true,
create: true,
schema: 'update',
},
forceUtcTimezone: true,
pool: {
min: isProduction ? 5 : 2,
max: isProduction ? 20 : 10,
idleTimeoutMillis: 30000,
acquireTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createTimeoutMillis: 15000,
destroyTimeoutMillis: 5000,
},
// 5. Use console for CLI logging, not the injected Nest logger
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
logger: isProduction ? console.log.bind(console) : console.debug.bind(console),
schemaGenerator: {
disableForeignKeys: false,
},
migrations: {
path: './database/migrations',
pathTs: './database/migrations',
tableName: 'migrations',
transactional: true,
allOrNothing: true,
dropTables: false,
safe: true,
emit: 'ts',
},
connect: true,
allowGlobalContext: true,
seeder: {
path: './dist/seeders',
pathTs: './src/seeders',
defaultSeeder: 'DatabaseSeeder',
},
driverOptions: {
connection: {
keepAlive: true,
keepAliveInitialDelayMillis: 10000,
statement_timeout: 60000,
query_timeout: 60000,
idle_in_transaction_session_timeout: 60000,
connectionTimeoutMillis: 10000,
application_name: DB_NAME,
},
connectionRetries: 5,
connectionRetryDelay: 3000,
validateConnection: (connection: any) => {
try {
return !!(connection && !connection.closed);
} catch (e: unknown) {
// Use console.error directly
console.error(`Connection validation failed: ${(e as Error).message}`);
return false;
}
},
poolErrorHandler: (err: Error) => {
// Use console.error directly
console.error(`PostgreSQL pool error: ${err.message}`);
if (err.message.includes('ECONNRESET') || err.message.includes('Connection terminated unexpectedly')) {
console.warn('Connection reset detected, will attempt to reconnect');
return true;
}
return false;
},
},
});
+109
View File
@@ -0,0 +1,109 @@
import type { MikroOrmModuleAsyncOptions } from '@mikro-orm/nestjs';
import { ConfigService } from '@nestjs/config';
import { PostgreSqlDriver } from '@mikro-orm/postgresql';
// import { defineConfig } from '@mikro-orm/postgresql';
import type { Logger } from '@nestjs/common';
import { MIKRO_ORM_QUERY_LOGGER } from '../common/constants';
import { mikroOrmQueryLoggerProvider } from '../common/providers/mikro-orm-logger.provider';
const dataBaseConfig: MikroOrmModuleAsyncOptions = {
// entities: ['dist/**/*.entity.js'],
// entitiesTs: ['src/**/*.entity.ts'],
// dbName: 'your_db_name',
// type: 'postgresql',
// user: 'your_user',
// password: 'your_password',
// host: 'localhost',
// port: 5432,
// debug: true,
inject: [ConfigService, MIKRO_ORM_QUERY_LOGGER],
providers: [mikroOrmQueryLoggerProvider],
useFactory: (configService: ConfigService, logger: Logger) => {
const DB_PASS = configService.getOrThrow<string>('DB_PASS');
const DB_USER = configService.getOrThrow<string>('DB_USER');
const DB_HOST = configService.getOrThrow<string>('DB_HOST');
const DB_PORT = configService.getOrThrow<number>('DB_PORT');
const DB_NAME = configService.getOrThrow<string>('DB_NAME');
const encodedPassword = encodeURIComponent(DB_PASS);
const isProduction = configService.getOrThrow<string>('NODE_ENV') === 'production';
return {
// entities: ['dist/**/*.entity.js'],
entitiesTs: ['src/**/*.entity.ts'],
driver: PostgreSqlDriver,
autoLoadEntities: true,
dbName: DB_NAME,
debug: false,
clientUrl: `postgres://${DB_USER}:${encodedPassword}@${DB_HOST}:${DB_PORT}`,
ensureDatabase: isProduction
? false
: {
forceCheck: true,
create: true,
schema: 'update',
},
forceUtcTimezone: true,
pool: {
min: isProduction ? 5 : 2,
max: isProduction ? 20 : 10,
idleTimeoutMillis: 30000,
acquireTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createTimeoutMillis: 15000,
destroyTimeoutMillis: 5000,
},
logger: isProduction ? message => logger.log(message) : message => logger.debug(message),
schemaGenerator: {
// createForeignKey: !isProduction,
disableForeignKeys: false,
// createIndex: true,
},
migrations: {
path: './database/migrations',
pathTs: './database/migrations',
tableName: 'migrations',
transactional: true,
allOrNothing: true,
dropTables: false,
safe: true,
emit: 'ts',
},
connect: true,
allowGlobalContext: true,
driverOptions: {
connection: {
keepAlive: true,
keepAliveInitialDelayMillis: 10000,
statement_timeout: 60000,
query_timeout: 60000,
idle_in_transaction_session_timeout: 60000,
connectionTimeoutMillis: 10000,
application_name: configService.getOrThrow<string>('DB_NAME'),
},
connectionRetries: 5,
connectionRetryDelay: 3000,
validateConnection: (connection: any) => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return !!(connection && !connection.closed);
} catch (e: unknown) {
logger.error(`Connection validation failed: ${(e as Error).message}`);
return false;
}
},
poolErrorHandler: (err: Error) => {
logger.error(`PostgreSQL pool error: ${err.message}`);
if (err.message.includes('ECONNRESET') || err.message.includes('Connection terminated unexpectedly')) {
logger.warn('Connection reset detected, will attempt to reconnect');
return true;
}
return false;
},
},
};
},
};
export default dataBaseConfig;
+27
View File
@@ -0,0 +1,27 @@
import type { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
export function getSwaggerConfig(app: INestApplication) {
const config = new DocumentBuilder()
.setTitle('D-menu API')
.setDescription('API documentation for D-menu backend')
.setVersion('1.0')
.addBearerAuth() // optional: for JWT endpoints
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document, {
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
showExtensions: true,
docExpansion: 'none',
// HIDE MODELS / SCHEMAS
defaultModelsExpandDepth: -1,
defaultModelExpandDepth: -1,
},
});
}