Logging Update - IPadress & user Email if possible

This commit is contained in:
Andreas Knuth 2024-09-23 13:45:46 +02:00
parent 974a6503ef
commit 1282d30b49
16 changed files with 163 additions and 22 deletions

View File

@ -19,7 +19,10 @@ import { EventModule } from './event/event.module';
import { JwtStrategy } from './jwt.strategy';
import { MailModule } from './mail/mail.module';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ClsMiddleware, ClsModule } from 'nestjs-cls';
import { LoggingInterceptor } from './interceptors/logging.interceptor';
import { UserInterceptor } from './interceptors/user.interceptor';
import { PaymentModule } from './payment/payment.module';
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware';
import { SelectOptionsModule } from './select-options/select-options.module';
@ -71,7 +74,19 @@ console.log(JSON.stringify(process.env, null, 2));
EventModule,
],
controllers: [AppController, LogController],
providers: [AppService, FileService, JwtStrategy],
providers: [
AppService,
FileService,
JwtStrategy,
{
provide: APP_INTERCEPTOR,
useClass: UserInterceptor, // Registriere den Interceptor global
},
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor, // Registriere den LoggingInterceptor global
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {

View File

@ -25,7 +25,8 @@ const { Pool } = pkg;
logQuery(query: string, params: unknown[]): void {
const ip = cls.get('ip') || 'unknown';
const countryCode = cls.get('countryCode') || 'unknown';
logger.info(`IP: ${ip} (${countryCode}) - Query: ${query} - Params: ${JSON.stringify(params)}`);
const username = cls.get('username') || 'unknown';
logger.info(`IP: ${ip} (${countryCode}) (${username}) - Query: ${query} - Params: ${JSON.stringify(params)}`);
},
};

View File

@ -1,5 +1,6 @@
import { Body, Controller, Headers, Post } from '@nestjs/common';
import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common';
import { RealIp } from 'src/decorators/real-ip.decorator';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { ListingEvent } from 'src/models/db.model';
import { RealIpInfo } from 'src/models/main.model';
import { EventService } from './event.service';
@ -7,6 +8,8 @@ import { EventService } from './event.service';
@Controller('event')
export class EventController {
constructor(private eventService: EventService) {}
@UseGuards(OptionalJwtAuthGuard)
@Post()
async createEvent(
@Body() event: ListingEvent, // Struktur des Body-Objekts entsprechend anpassen

View File

@ -1,5 +1,6 @@
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { RealIp } from 'src/decorators/real-ip.decorator';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { RealIpInfo } from 'src/models/main.model';
import { CountyRequest } from 'src/models/server.model';
import { GeoService } from './geo.service';
@ -8,24 +9,31 @@ import { GeoService } from './geo.service';
export class GeoController {
constructor(private geoService: GeoService) {}
@UseGuards(OptionalJwtAuthGuard)
@Get(':prefix')
findByPrefix(@Param('prefix') prefix: string): any {
return this.geoService.findCitiesStartingWith(prefix);
}
@UseGuards(OptionalJwtAuthGuard)
@Get('citiesandstates/:prefix')
findByCitiesAndStatesByPrefix(@Param('prefix') prefix: string): any {
return this.geoService.findCitiesAndStatesStartingWith(prefix);
}
@UseGuards(OptionalJwtAuthGuard)
@Get(':prefix/:state')
findByPrefixAndState(@Param('prefix') prefix: string, @Param('state') state: string): any {
return this.geoService.findCitiesStartingWith(prefix, state);
}
@UseGuards(OptionalJwtAuthGuard)
@Post('counties')
findByPrefixAndStates(@Body() countyRequest: CountyRequest): any {
return this.geoService.findCountiesStartingWith(countyRequest.prefix, countyRequest.states);
}
@UseGuards(OptionalJwtAuthGuard)
@Get('ipinfo/georesult/wysiwyg')
async fetchIpAndGeoLocation(@RealIp() ipInfo: RealIpInfo): Promise<any> {
return await this.geoService.fetchIpAndGeoLocation(ipInfo);

View File

@ -0,0 +1,40 @@
// src/interceptors/logging.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
constructor(private readonly cls: ClsService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const ip = this.cls.get('ip') || 'unknown';
const countryCode = this.cls.get('countryCode') || 'unknown';
const username = this.cls.get('username') || 'unknown';
const method = request.method;
const url = request.originalUrl;
const start = Date.now();
this.logger.log(`Entering ${method} ${url} from ${ip} (${countryCode})- User: ${username}`);
return next.handle().pipe(
tap(() => {
const duration = Date.now() - start;
let logMessage = `${method} ${url} - ${duration}ms - IP: ${ip} - User: ${username}`;
if (method === 'POST' || method === 'PUT') {
const body = JSON.stringify(request.body);
logMessage += ` - Incoming Body: ${body}`;
}
this.logger.log(logMessage);
}),
);
}
}

View File

@ -0,0 +1,29 @@
// src/interceptors/user.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import { Observable } from 'rxjs';
@Injectable()
export class UserInterceptor implements NestInterceptor {
private readonly logger = new Logger(UserInterceptor.name);
constructor(private readonly cls: ClsService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
// Überprüfe, ob der Benutzer authentifiziert ist
if (request.user && request.user.username) {
try {
this.cls.set('username', request.user.username);
this.logger.log(`CLS context gesetzt: Username=${request.user.username}`);
} catch (error) {
this.logger.error('Fehler beim Setzen der Username im CLS-Kontext', error);
}
} else {
this.logger.log('Kein authentifizierter Benutzer gefunden');
}
return next.handle();
}
}

View File

@ -1,5 +1,6 @@
import { Body, Controller, Inject, Post } from '@nestjs/common';
import { Body, Controller, Inject, Post, UseGuards } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { UserListingCriteria } from 'src/models/main.model';
import { Logger } from 'winston';
import { UserService } from '../user/user.service';
@ -11,6 +12,7 @@ export class BrokerListingsController {
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
@UseGuards(OptionalJwtAuthGuard)
@Post('search')
async find(@Body() criteria: UserListingCriteria): Promise<any> {
return await this.userService.searchUserListings(criteria);

View File

@ -41,18 +41,24 @@ export class BusinessListingsController {
return await this.listingsService.getBusinessListingsCount(criteria, req.user as JwtUser);
}
@UseGuards(OptionalJwtAuthGuard)
@Post()
async create(@Body() listing: any) {
return await this.listingsService.createListing(listing);
}
@UseGuards(OptionalJwtAuthGuard)
@Put()
async update(@Body() listing: any) {
return await this.listingsService.updateBusinessListing(listing.id, listing);
}
@UseGuards(OptionalJwtAuthGuard)
@Delete('listing/:id')
async deleteById(@Param('id') id: string) {
await this.listingsService.deleteListing(id);
}
@UseGuards(JwtAuthGuard)
@Delete('favorite/:id')
async deleteFavorite(@Request() req, @Param('id') id: string) {

View File

@ -21,16 +21,19 @@ export class CommercialPropertyListingsController {
async findById(@Request() req, @Param('id') id: string): Promise<any> {
return await this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser);
}
@UseGuards(JwtAuthGuard)
@Get('favorites/all')
async findFavorites(@Request() req): Promise<any> {
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
}
@UseGuards(OptionalJwtAuthGuard)
@Get('user/:email')
async findByEmail(@Request() req, @Param('email') email: string): Promise<CommercialPropertyListing[]> {
return await this.listingsService.findCommercialPropertiesByEmail(email, req.user as JwtUser);
}
@UseGuards(OptionalJwtAuthGuard)
@Post('find')
async find(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<any> {
@ -41,14 +44,20 @@ export class CommercialPropertyListingsController {
async findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<number> {
return await this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser);
}
@UseGuards(OptionalJwtAuthGuard)
@Post()
async create(@Body() listing: any) {
return await this.listingsService.createListing(listing);
}
@UseGuards(OptionalJwtAuthGuard)
@Put()
async update(@Body() listing: any) {
return await this.listingsService.updateCommercialPropertyListing(listing.id, listing);
}
@UseGuards(OptionalJwtAuthGuard)
@Delete('listing/:id/:imagePath')
async deleteById(@Param('id') id: string, @Param('imagePath') imagePath: string) {
await this.listingsService.deleteListing(id);

View File

@ -1,4 +1,5 @@
import { Body, Controller, Post } from '@nestjs/common';
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { ShareByEMail, User } from 'src/models/db.model';
import { ErrorResponse, MailInfo } from '../models/main.model';
import { MailService } from './mail.service';
@ -6,6 +7,8 @@ import { MailService } from './mail.service';
@Controller('mail')
export class MailController {
constructor(private mailService: MailService) {}
@UseGuards(OptionalJwtAuthGuard)
@Post()
async sendEMail(@Body() mailInfo: MailInfo): Promise<void | ErrorResponse> {
if (mailInfo.listing) {
@ -14,10 +17,14 @@ export class MailController {
return await this.mailService.sendRequest(mailInfo);
}
}
@UseGuards(OptionalJwtAuthGuard)
@Post('subscriptionConfirmation')
async sendSubscriptionConfirmation(@Body() user: User): Promise<void | ErrorResponse> {
return await this.mailService.sendSubscriptionConfirmation(user);
}
@UseGuards(OptionalJwtAuthGuard)
@Post('send2Friend')
async send2Friend(@Body() shareByEMail: ShareByEMail): Promise<void | ErrorResponse> {
return await this.mailService.send2Friend(shareByEMail);

View File

@ -1,6 +1,7 @@
import { Body, Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Param, Post, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { AdminAuthGuard } from 'src/jwt-auth/admin-auth.guard';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { Checkout } from 'src/models/main.model';
import Stripe from 'stripe';
import { PaymentService } from './payment.service';
@ -19,16 +20,20 @@ export class PaymentController {
async getAllStripeCustomer(): Promise<Stripe.Customer[]> {
return await this.paymentService.getAllStripeCustomer();
}
@UseGuards(AdminAuthGuard)
@Get('subscription/all')
async getAllStripeSubscriptions(): Promise<Stripe.Subscription[]> {
return await this.paymentService.getAllStripeSubscriptions();
}
@UseGuards(AdminAuthGuard)
@Get('paymentmethod/:email')
async getStripePaymentMethods(@Param('email') email: string): Promise<Stripe.PaymentMethod[]> {
return await this.paymentService.getStripePaymentMethod(email);
}
@UseGuards(OptionalJwtAuthGuard)
@Post('create-checkout-session')
async createCheckoutSession(@Body() checkout: Checkout) {
return await this.paymentService.createCheckoutSession(checkout);
@ -53,6 +58,8 @@ export class PaymentController {
throw new HttpException('Webhook Error', HttpStatus.BAD_REQUEST);
}
}
@UseGuards(OptionalJwtAuthGuard)
@Get('subscriptions/:email')
async findSubscriptionsById(@Param('email') email: string): Promise<any> {
return await this.paymentService.getSubscription(email);

View File

@ -20,21 +20,22 @@ export class RequestDurationMiddleware implements NestMiddleware {
this.logger.error('Failed to set CLS context', error);
}
const start = Date.now();
// const start = Date.now();
this.logger.log(`Entering ${req.method} ${req.originalUrl} from ${ip}`);
// this.logger.log(`Entering ${req.method} ${req.originalUrl} from ${ip}`);
res.on('finish', () => {
const duration = Date.now() - start;
let logMessage = `${req.method} ${req.originalUrl} - ${duration}ms - IP: ${ip}`;
// res.on('finish', () => {
// const duration = Date.now() - start;
// const userEmail = this.cls.get('userEmail') || 'unknown';
// let logMessage = `${req.method} ${req.originalUrl} - ${duration}ms - IP: ${ip} - User: ${userEmail}`;
if (req.method === 'POST' || req.method === 'PUT') {
const body = JSON.stringify(req.body);
logMessage += ` - Incoming Body: ${body}`;
}
// if (req.method === 'POST' || req.method === 'PUT') {
// const body = JSON.stringify(req.body);
// logMessage += ` - Incoming Body: ${body}`;
// }
this.logger.log(logMessage);
});
// this.logger.log(logMessage);
// });
next();
}

View File

@ -1,9 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, UseGuards } from '@nestjs/common';
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
import { SelectOptionsService } from './select-options.service';
@Controller('select-options')
export class SelectOptionsController {
constructor(private selectOptionsService: SelectOptionsService) {}
@UseGuards(OptionalJwtAuthGuard)
@Get()
getSelectOption(): any {
return {

View File

@ -17,6 +17,7 @@ export class UserController {
private fileService: FileService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
@UseGuards(OptionalJwtAuthGuard)
@Get()
async findByMail(@Request() req, @Query('mail') mail: string): Promise<User> {
@ -24,6 +25,7 @@ export class UserController {
return user;
}
@UseGuards(OptionalJwtAuthGuard)
@Get(':id')
async findById(@Param('id') id: string): Promise<User> {
const user = await this.userService.getUserById(id);
@ -34,6 +36,8 @@ export class UserController {
async getAllUser(): Promise<User[]> {
return await this.userService.getAllUser();
}
@UseGuards(OptionalJwtAuthGuard)
@Post()
async save(@Body() user: any): Promise<User> {
try {
@ -52,16 +56,22 @@ export class UserController {
throw error; // Andere Fehler einfach durchreichen
}
}
@UseGuards(OptionalJwtAuthGuard)
@Post('guaranteed')
async saveGuaranteed(@Body() user: any): Promise<User> {
const savedUser = await this.userService.saveUser(user, false);
return savedUser;
}
@UseGuards(OptionalJwtAuthGuard)
@Post('search')
async find(@Body() criteria: UserListingCriteria): Promise<{ results: User[]; totalCount: number }> {
const foundUsers = await this.userService.searchUserListings(criteria);
return foundUsers;
}
@UseGuards(OptionalJwtAuthGuard)
@Post('findTotal')
async findTotal(@Body() criteria: UserListingCriteria): Promise<number> {
return await this.userService.getUserListingsCount(criteria);

View File

@ -19,7 +19,7 @@
<tr class="border-b">
<td class="py-2 px-4">{{ listing.title }}</td>
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
<td class="py-2 px-4">{{ listing.location.name }}, {{ listing.location.state }}</td>
<td class="py-2 px-4">{{ listing.location.name ? listing.location.name : listing.location.county }}, {{ listing.location.state }}</td>
<td class="py-2 px-4">${{ listing.price.toLocaleString() }}</td>
<td class="py-2 px-4 flex">
@if(listing.listingsCategory==='business'){
@ -47,7 +47,7 @@
<div *ngFor="let listing of favorites" class="bg-white shadow-md rounded-lg p-4 mb-4">
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name }}, {{ listing.location.state }}</p>
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name ? listing.location.name : listing.location.county }}, {{ listing.location.state }}</p>
<p class="text-gray-600 mb-2">Price: ${{ listing.price.toLocaleString() }}</p>
<div class="flex justify-start">
@if(listing.listingsCategory==='business'){

View File

@ -18,7 +18,7 @@
<tr *ngFor="let listing of myListings" class="border-b">
<td class="py-2 px-4">{{ listing.title }}</td>
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
<td class="py-2 px-4">{{ listing.location.name }}, {{ listing.location.state }}</td>
<td class="py-2 px-4">{{ listing.location.name ? listing.location.name : listing.location.county }}, {{ listing.location.state }}</td>
<td class="py-2 px-4">${{ listing.price.toLocaleString() }}</td>
<td class="py-2 px-4">
@if(listing.listingsCategory==='business'){
@ -54,7 +54,7 @@
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name }} - {{ listing.location.state }}</p>
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name ? listing.location.name : listing.location.county }} - {{ listing.location.state }}</p>
<p class="text-gray-600 mb-2">Price: ${{ listing.price.toLocaleString() }}</p>
<div class="flex justify-start">
@if(listing.listingsCategory==='business'){