BugFix: Proxy data, Logging with IP adresses
This commit is contained in:
parent
178f2b4810
commit
860d30b16f
|
|
@ -19,6 +19,7 @@ import { EventModule } from './event/event.module';
|
|||
import { JwtStrategy } from './jwt.strategy';
|
||||
import { MailModule } from './mail/mail.module';
|
||||
|
||||
import { ContextService } from './context/context.service';
|
||||
import { PaymentModule } from './payment/payment.module';
|
||||
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware';
|
||||
import { SelectOptionsModule } from './select-options/select-options.module';
|
||||
|
|
@ -63,7 +64,7 @@ console.log(JSON.stringify(process.env, null, 2));
|
|||
EventModule,
|
||||
],
|
||||
controllers: [AppController, LogController],
|
||||
providers: [AppService, FileService, JwtStrategy],
|
||||
providers: [AppService, FileService, JwtStrategy, ContextService, ContextService],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import { createParamDecorator, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { RealIpInfo } from './models/main.model';
|
||||
import { getRealIpInfo } from './utils/ip.util';
|
||||
// export const RealIp = createParamDecorator((data: unknown, ctx: ExecutionContext): RealIpInfo => {
|
||||
// const request = ctx.switchToHttp().getRequest();
|
||||
// const ip = request.headers['cf-connecting-ip'] || request.headers['x-real-ip'] || request.headers['x-forwarded-for']?.split(',')[0] || request.connection.remoteAddress;
|
||||
// const countryCode = request.headers['cf-ipcountry'];
|
||||
// return { ip, countryCode };
|
||||
// });
|
||||
export const RealIp = createParamDecorator((data: unknown, ctx: ExecutionContext): RealIpInfo => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
const ip = request.headers['cf-connecting-ip'] || request.headers['x-real-ip'] || request.headers['x-forwarded-for']?.split(',')[0] || request.connection.remoteAddress;
|
||||
const countryCode = request.headers['cf-ipcountry'];
|
||||
return { ip, countryCode };
|
||||
return getRealIpInfo(request);
|
||||
});
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// src/context/context.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
|
||||
export interface RequestContext {
|
||||
ip: string;
|
||||
countryCode?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ContextService {
|
||||
private readonly asyncLocalStorage = new AsyncLocalStorage<RequestContext>();
|
||||
|
||||
run(context: RequestContext, callback: () => void) {
|
||||
this.asyncLocalStorage.run(context, callback);
|
||||
}
|
||||
|
||||
getContext(): RequestContext | undefined {
|
||||
return this.asyncLocalStorage.getStore();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,16 +3,18 @@ import { ConfigService } from '@nestjs/config';
|
|||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import pkg from 'pg';
|
||||
import { ContextService, RequestContext } from 'src/context/context.service';
|
||||
import { Logger } from 'winston';
|
||||
import * as schema from './schema';
|
||||
import { PG_CONNECTION } from './schema';
|
||||
const { Pool } = pkg;
|
||||
@Module({
|
||||
providers: [
|
||||
ContextService,
|
||||
{
|
||||
provide: PG_CONNECTION,
|
||||
inject: [ConfigService, WINSTON_MODULE_PROVIDER],
|
||||
useFactory: async (configService: ConfigService, logger: Logger) => {
|
||||
inject: [ConfigService, WINSTON_MODULE_PROVIDER, ContextService],
|
||||
useFactory: async (configService: ConfigService, logger: Logger, contextService: ContextService) => {
|
||||
const connectionString = configService.get<string>('DATABASE_URL');
|
||||
const pool = new Pool({
|
||||
connectionString,
|
||||
|
|
@ -21,7 +23,10 @@ const { Pool } = pkg;
|
|||
// Definiere einen benutzerdefinierten Logger für Drizzle
|
||||
const drizzleLogger = {
|
||||
logQuery(query: string, params: unknown[]): void {
|
||||
logger.info(query, params);
|
||||
const context: RequestContext | undefined = contextService.getContext();
|
||||
const ip = context?.ip || 'unknown';
|
||||
const countryCode = context?.countryCode || 'unknown';
|
||||
logger.info(`IP: ${ip} (${countryCode}) - Query: ${query} - Params: ${JSON.stringify(params)}`);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ async function bootstrap() {
|
|||
|
||||
app.enableCors({
|
||||
origin: '*',
|
||||
//origin: 'http://localhost:4200', // Die URL Ihrer Angular-App
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
allowedHeaders: 'Content-Type, Accept, Authorization, x-hide-loading',
|
||||
});
|
||||
//origin: 'http://localhost:4200',
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
|
|
|
|||
|
|
@ -1,27 +1,36 @@
|
|||
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { ContextService } from 'src/context/context.service';
|
||||
import { getRealIpInfo } from 'src/utils/ip.util';
|
||||
|
||||
@Injectable()
|
||||
export class RequestDurationMiddleware implements NestMiddleware {
|
||||
private readonly logger = new Logger(RequestDurationMiddleware.name);
|
||||
|
||||
constructor(private readonly contextService: ContextService) {}
|
||||
|
||||
use(req: Request, res: Response, next: NextFunction) {
|
||||
const start = Date.now();
|
||||
const clientIp = req.ip;
|
||||
this.logger.log(`Entering ${req.method} ${req.originalUrl} from ${clientIp}`);
|
||||
const { ip, countryCode } = getRealIpInfo(req);
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
let logMessage = `${req.method} ${req.originalUrl} - ${duration}ms - IP: ${clientIp}`;
|
||||
// Initialisieren des Kontextes für diese Anfrage
|
||||
this.contextService.run({ ip, countryCode }, () => {
|
||||
const start = Date.now();
|
||||
|
||||
if (req.method === 'POST' || req.method === 'PUT') {
|
||||
const body = JSON.stringify(req.body);
|
||||
logMessage += ` - Incoming Body: ${body}`;
|
||||
}
|
||||
this.logger.log(`Entering ${req.method} ${req.originalUrl} from ${ip}`);
|
||||
|
||||
this.logger.log(logMessage);
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
let logMessage = `${req.method} ${req.originalUrl} - ${duration}ms - IP: ${ip}`;
|
||||
|
||||
if (req.method === 'POST' || req.method === 'PUT') {
|
||||
const body = JSON.stringify(req.body);
|
||||
logMessage += ` - Incoming Body: ${body}`;
|
||||
}
|
||||
|
||||
this.logger.log(logMessage);
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import { Request } from 'express';
|
||||
|
||||
export interface RealIpInfo {
|
||||
ip: string | undefined;
|
||||
countryCode?: string;
|
||||
}
|
||||
|
||||
export function getRealIpInfo(req: Request): RealIpInfo {
|
||||
const ip =
|
||||
(req.headers['cf-connecting-ip'] as string) ||
|
||||
(req.headers['x-real-ip'] as string) ||
|
||||
(typeof req.headers['x-forwarded-for'] === 'string' ? req.headers['x-forwarded-for'].split(',')[0] : req.connection.remoteAddress);
|
||||
const countryCode = req.headers['cf-ipcountry'] as string;
|
||||
|
||||
return { ip, countryCode };
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import { ListingsService } from '../../../services/listings.service';
|
|||
import { SearchService } from '../../../services/search.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { getCriteriaStateObject } from '../../../utils/utils';
|
||||
import { getCriteriaProxy } from '../../../utils/utils';
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'app-broker-listings',
|
||||
|
|
@ -54,7 +54,7 @@ export class BrokerListingsComponent {
|
|||
private route: ActivatedRoute,
|
||||
private searchService: SearchService,
|
||||
) {
|
||||
this.criteria = getCriteriaStateObject('brokerListings');
|
||||
this.criteria = getCriteriaProxy('brokerListings', this) as UserListingCriteria;
|
||||
this.init();
|
||||
this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => {
|
||||
if (criteria && criteria.criteriaType === 'brokerListings') {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ImageService } from '../../../services/image.service';
|
|||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SearchService } from '../../../services/search.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { getCriteriaStateObject } from '../../../utils/utils';
|
||||
import { getCriteriaProxy } from '../../../utils/utils';
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'app-business-listings',
|
||||
|
|
@ -50,7 +50,7 @@ export class BusinessListingsComponent {
|
|||
private route: ActivatedRoute,
|
||||
private searchService: SearchService,
|
||||
) {
|
||||
this.criteria = getCriteriaStateObject('businessListings');
|
||||
this.criteria = getCriteriaProxy('businessListings', this) as BusinessListingCriteria;
|
||||
this.init();
|
||||
this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => {
|
||||
if (criteria && criteria.criteriaType === 'businessListings') {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ImageService } from '../../../services/image.service';
|
|||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SearchService } from '../../../services/search.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { getCriteriaStateObject } from '../../../utils/utils';
|
||||
import { getCriteriaProxy } from '../../../utils/utils';
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'app-commercial-property-listings',
|
||||
|
|
@ -49,7 +49,7 @@ export class CommercialPropertyListingsComponent {
|
|||
private route: ActivatedRoute,
|
||||
private searchService: SearchService,
|
||||
) {
|
||||
this.criteria = getCriteriaStateObject('commercialPropertyListings');
|
||||
this.criteria = getCriteriaProxy('commercialPropertyListings', this) as CommercialPropertyListingCriteria;
|
||||
this.init();
|
||||
this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => {
|
||||
if (criteria && criteria.criteriaType === 'commercialPropertyListings') {
|
||||
|
|
|
|||
|
|
@ -335,6 +335,8 @@ export function createEnhancedProxy(obj: BusinessListingCriteria | CommercialPro
|
|||
sessionStorageHandler.call(this, path, value, previous, applyData);
|
||||
|
||||
// Notify about the criteria change using the component's context
|
||||
component.criteriaChangeService.notifyCriteriaChange();
|
||||
if (component.criteriaChangeService) {
|
||||
component.criteriaChangeService.notifyCriteriaChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue