Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
spuxx1701 committed Jan 15, 2024
2 parents c58eee4 + ac23827 commit 2bc01c4
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).

## [2.2.5] - 2024-01-15

### Added

- Implemented metrics endpoint for prometheus.

### Changed

- Request/response logging now utilizes both middlewares and interceptors to also log requests that were turned down (e.g. due to CORS).
Expand Down
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.4.3",
"@nestjs/swagger": "^6.3.0",
"@willsoto/nestjs-prometheus": "^6.0.0",
"axios": "^1.6.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
Expand Down
11 changes: 9 additions & 2 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { EncodingModule } from 'src/encoding/encoding.module';
import { HttpModule } from 'src/http/http.module';
Expand All @@ -8,15 +8,22 @@ import { AuthController } from './controllers/auth.controller';
import { AuthService } from './services/auth.service';
import { JwtStrategy } from './strategies/jwt.strategy';
import { UsersModule } from 'src/users/users.module';
import { appConfig } from 'src/config/app.config';
import { corsConfig } from 'src/config/cors.config';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [appConfig, corsConfig],
}),

EncodingModule,
HttpModule,
XmlApiModule,
JwtModule.registerAsync({
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('AUTH_JWT_SECRET'),
secret: configService.get<string>('application.auth.jwtSecret'),
}),
inject: [ConfigService],
}),
Expand Down
13 changes: 13 additions & 0 deletions src/config/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { registerAs } from '@nestjs/config';

export const appConfig = registerAs('application', () => ({
port: parseInt(process.env.APP_PORT, 10) || 3000,
metricsPort: parseInt(process.env.APP_METRICS_PORT, 10) || 9100,
clientUrl: process.env.APP_CLIENT_URL ?? 'https://potber.de',
apiUrl: process.env.APP_API_URL ?? 'https://api.potber.de',
auth: {
jwtSecret: process.env,
},
}));

export type AppConfig = typeof appConfig;
17 changes: 12 additions & 5 deletions src/config/cors.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
import { registerAs } from '@nestjs/config';

export const corsConfig: CorsOptions = {
allowedHeaders: ['content-type', 'authorization'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
};
export const corsConfig = registerAs(
'cors',
(): CorsOptions => ({
allowedHeaders: ['content-type', 'authorization'],
origin: (process.env.CORS_ALLOWED_ORIGINS ?? '').split(','),
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
}),
);

export type CorsConfig = typeof corsConfig;
29 changes: 19 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import { ConfigService } from '@nestjs/config';
import { ConfigService, ConfigType } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { corsConfig } from './config/cors.config';
import {
swaggerConfig,
swaggerOptions,
swaggerUri,
} from './config/swagger.config';
import { Logger } from '@nestjs/common';
import { ResponseLoggingInterceptor } from './log/response.logging.interceptor';
import { MonitoringExternalModule } from './monitoring/monitoring.external-module';
import { AppConfig } from './config/app.config';
import { CorsConfig } from './config/cors.config';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

const configService = app.get(ConfigService);
const appConfig = configService.get<ConfigType<AppConfig>>('application');
const corsConfig = configService.get<ConfigType<CorsConfig>>('cors');

// Set up CORS
const origin = configService.get<string>('CORS_ALLOWED_ORIGINS').split(',');
Logger.log(
`CORS enabled. The following origins will be allowed: '${origin.join(
"', '",
)}'.`,
'NestApplication',
`CORS enabled. The following origins will be allowed: '${corsConfig.origin}'.`,
'Bootstrap',
);
app.enableCors({
origin,
...corsConfig,
});

Expand All @@ -37,8 +37,17 @@ async function bootstrap() {

app.useGlobalInterceptors(new ResponseLoggingInterceptor());

const port = configService.get<number>('APP_PORT');
const { port } = appConfig;
await app.listen(port);
Logger.log(`Application is listening on port ${port}.`, 'NestApplication');
Logger.log(`Application is listening on port ${port}.`, 'Bootstrap');

// Start monitoring process
const mon = await NestFactory.create(MonitoringExternalModule);
const { metricsPort } = appConfig;
await mon.listen(metricsPort);
Logger.log(
`Metrics are available at '/metrics' and port ${metricsPort}.`,
'Bootstrap',
);
}
bootstrap();
20 changes: 20 additions & 0 deletions src/monitoring/monitoring.dummy-controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createTestContainer } from 'test/container';
import { MonitoringDummyController } from './monitoring.dummy-controller';

describe('Monitoring | Dummy controller', () => {
let controller: MonitoringDummyController;

beforeEach(async () => {
const container = await createTestContainer({
controllers: [MonitoringDummyController],
});

controller = container.module.get<MonitoringDummyController>(
MonitoringDummyController,
);
});

it('should build', () => {
expect(controller).toBeInstanceOf(MonitoringDummyController);
});
});
9 changes: 9 additions & 0 deletions src/monitoring/monitoring.dummy-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller } from '@nestjs/common';

/**
* This is an empty controller that is used to disable the default /metrics route.
* /metrics should not be available within the application context, but instead
* will be exposed on a different node process.
*/
@Controller()
export class MonitoringDummyController {}
18 changes: 18 additions & 0 deletions src/monitoring/monitoring.external-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';

/**
* The external monitoring module does not define any metrics itself, and instead
* only exposed the /metrics route. The module will be served on a seperate process
* and listens to another port.
*/
@Module({
imports: [
PrometheusModule.register({
defaultMetrics: {
enabled: true,
},
}),
],
})
export class MonitoringExternalModule {}
16 changes: 16 additions & 0 deletions src/monitoring/monitoring.module-internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { MonitoringDummyController } from './monitoring.dummy-controller';

/**
* The internal monitoring module does not expose a /metrics route, but defines and
* collects metrics from the application. It is included into the main application module.
*/
@Module({
imports: [
PrometheusModule.register({
controller: MonitoringDummyController,
}),
],
})
export class MonitoringInteralModule {}

0 comments on commit 2bc01c4

Please sign in to comment.