diff --git a/bizmatch-server/.env.development b/bizmatch-server/.env.development new file mode 100644 index 0000000..f254a52 --- /dev/null +++ b/bizmatch-server/.env.development @@ -0,0 +1,4 @@ +REALM=bizmatch-dev +usersURL=/admin/realms/bizmatch-dev/users +WEB_HOST=https://dev.bizmatch.net +STRIPE_WEBHOOK_SECRET=whsec_w2yvJY8qFMfO5wJgyNHCn6oYT7o2J5pS \ No newline at end of file diff --git a/bizmatch-server/.env.production b/bizmatch-server/.env.production new file mode 100644 index 0000000..79289c2 --- /dev/null +++ b/bizmatch-server/.env.production @@ -0,0 +1,2 @@ +REALM=bizmatch +WEB_HOST=https://www.bizmatch.net \ No newline at end of file diff --git a/bizmatch-server/package.json b/bizmatch-server/package.json index fb5c83b..b0be50e 100644 --- a/bizmatch-server/package.json +++ b/bizmatch-server/package.json @@ -8,11 +8,11 @@ "scripts": { "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "HOST_NAME=localhost nest start", + "start": "nest start", "start:local": "HOST_NAME=localhost node dist/src/main", - "start:dev": "HOST_NAME=dev.bizmatch.net node dist/src/main", + "start:dev": "NODE_ENV=development node dist/src/main", "start:debug": "nest start --debug --watch", - "start:prod": "HOST_NAME=www.bizmatch.net node dist/src/main", + "start:prod": "NODE_ENV=production node dist/src/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", @@ -38,6 +38,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "dotenv": "^16.4.5", + "dotenv-flow": "^4.1.0", "drizzle-orm": "^0.32.0", "fs-extra": "^11.2.0", "groq-sdk": "^0.5.0", diff --git a/bizmatch-server/src/app.module.ts b/bizmatch-server/src/app.module.ts index ecadae0..cd9750e 100644 --- a/bizmatch-server/src/app.module.ts +++ b/bizmatch-server/src/app.module.ts @@ -1,8 +1,6 @@ import { MiddlewareConsumer, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { PassportModule } from '@nestjs/passport'; -import * as dotenv from 'dotenv'; -import fs from 'fs-extra'; import { WinstonModule, utilities as nestWinstonModuleUtilities } from 'nest-winston'; import * as winston from 'winston'; import { AiModule } from './ai/ai.module'; @@ -16,46 +14,48 @@ import { ListingsModule } from './listings/listings.module'; import { LogController } from './log/log.controller'; import { LogModule } from './log/log.module'; +import dotenvFlow from 'dotenv-flow'; +import { EventModule } from './event/event.module'; import { JwtStrategy } from './jwt.strategy'; import { MailModule } from './mail/mail.module'; import { PaymentModule } from './payment/payment.module'; import { RequestDurationMiddleware } from './request-duration/request-duration.middleware'; import { SelectOptionsModule } from './select-options/select-options.module'; import { UserModule } from './user/user.module'; -import { EventModule } from './event/event.module'; -// const __filename = fileURLToPath(import.meta.url); -// const __dirname = path.dirname(__filename); -function loadEnvFiles() { - // Determine which additional env file to load - let envFilePath = ''; - const host = process.env.HOST_NAME || ''; +// function loadEnvFiles() { +// // Determine which additional env file to load +// let envFilePath = ''; +// const host = process.env.HOST_NAME || ''; - if (host.includes('localhost')) { - envFilePath = '.env.local'; - } else if (host.includes('dev.bizmatch.net')) { - envFilePath = '.env.dev'; - } else if (host.includes('www.bizmatch.net') || host.includes('bizmatch.net')) { - envFilePath = '.env.prod'; - } +// if (host.includes('localhost')) { +// envFilePath = '.env.local'; +// } else if (host.includes('dev.bizmatch.net')) { +// envFilePath = '.env.dev'; +// } else if (host.includes('www.bizmatch.net') || host.includes('bizmatch.net')) { +// envFilePath = '.env.prod'; +// } - // Load the additional env file if it exists - if (fs.existsSync(envFilePath)) { - dotenv.config({ path: envFilePath }); - console.log(`Loaded ${envFilePath} file`); - } else { - console.log(`No additional .env file found for HOST_NAME: ${host}`); - } +// // Load the additional env file if it exists +// if (fs.existsSync(envFilePath)) { +// dotenv.config({ path: envFilePath }); +// console.log(`Loaded ${envFilePath} file`); +// } else { +// console.log(`No additional .env file found for HOST_NAME: ${host}`); +// } - // Load the .env file - dotenv.config(); - console.log('Loaded .env file'); - // Output all loaded environment variables - console.log('Loaded environment variables:'); - console.log(JSON.stringify(process.env, null, 2)); -} +// // Load the .env file +// dotenv.config(); +// console.log('Loaded .env file'); +// // Output all loaded environment variables +// console.log('Loaded environment variables:'); +// console.log(JSON.stringify(process.env, null, 2)); +// } -loadEnvFiles(); +//loadEnvFiles(); +dotenvFlow.config(); +console.log('Loaded environment variables:'); +console.log(JSON.stringify(process.env, null, 2)); @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), @@ -65,7 +65,9 @@ loadEnvFiles(); transports: [ new winston.transports.Console({ format: winston.format.combine( - winston.format.timestamp(), + winston.format.timestamp({ + format: 'YYYY-MM-DD hh:mm:ss.SSS A', + }), winston.format.ms(), nestWinstonModuleUtilities.format.nestLike('Bizmatch', { colors: true, diff --git a/bizmatch-server/src/auth/auth.controller.ts b/bizmatch-server/src/auth/auth.controller.ts index 50d0d84..f1fb6fa 100644 --- a/bizmatch-server/src/auth/auth.controller.ts +++ b/bizmatch-server/src/auth/auth.controller.ts @@ -10,25 +10,25 @@ export class AuthController { @UseGuards(AdminAuthGuard) @Get() - getAccessToken(): any { - return this.authService.getAccessToken(); + async getAccessToken(): Promise { + return await this.authService.getAccessToken(); } @UseGuards(AdminAuthGuard) @Get('user/all') - getUsers(): any { - return this.authService.getUsers(); + async getUsers(): Promise { + return await this.authService.getUsers(); } @UseGuards(JwtAuthGuard) @Get('users/:userid') - getUser(@Param('userid') userId: string): any { - return this.authService.getUser(userId); + async getUser(@Param('userid') userId: string): Promise { + return await this.authService.getUser(userId); } @UseGuards(JwtAuthGuard) @Put('users/:userid') - updateKeycloakUser(@Body() keycloakUser: KeycloakUser): any { - return this.authService.updateKeycloakUser(keycloakUser); + async updateKeycloakUser(@Body() keycloakUser: KeycloakUser): Promise { + return await this.authService.updateKeycloakUser(keycloakUser); } // @UseGuards(AdminAuthGuard) // @Get('user/:userid/lastlogin') //e0811669-c7eb-4e5e-a699-e8334d5c5b01 -> aknuth diff --git a/bizmatch-server/src/drizzle/drizzle.module.ts b/bizmatch-server/src/drizzle/drizzle.module.ts index 6c67523..319ce9c 100644 --- a/bizmatch-server/src/drizzle/drizzle.module.ts +++ b/bizmatch-server/src/drizzle/drizzle.module.ts @@ -1,7 +1,9 @@ import { Module } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { drizzle } from 'drizzle-orm/node-postgres'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import pkg from 'pg'; +import { Logger } from 'winston'; import * as schema from './schema'; import { PG_CONNECTION } from './schema'; const { Pool } = pkg; @@ -9,15 +11,21 @@ const { Pool } = pkg; providers: [ { provide: PG_CONNECTION, - inject: [ConfigService], - useFactory: async (configService: ConfigService) => { + inject: [ConfigService, WINSTON_MODULE_PROVIDER], + useFactory: async (configService: ConfigService, logger: Logger) => { const connectionString = configService.get('DATABASE_URL'); const pool = new Pool({ connectionString, // ssl: true, }); + // Definiere einen benutzerdefinierten Logger für Drizzle + const drizzleLogger = { + logQuery(query: string, params: unknown[]): void { + logger.info(query, params); + }, + }; - return drizzle(pool, { schema, logger: true }); + return drizzle(pool, { schema, logger: drizzleLogger }); }, }, ], diff --git a/bizmatch-server/src/event/event.controller.ts b/bizmatch-server/src/event/event.controller.ts index 5e062e6..e2b256c 100644 --- a/bizmatch-server/src/event/event.controller.ts +++ b/bizmatch-server/src/event/event.controller.ts @@ -11,10 +11,9 @@ export class EventController { @Ip() userIp: string, // IP Adresse des Clients @Headers('user-agent') userAgent: string, // User-Agent des Clients ) { - //const { listingId, userId, eventType, locationCountry, locationCity, locationLat, locationLng, referrer, additionalData } = body; event.userIp = userIp; event.userAgent = userAgent; - this.eventService.createEvent(event); + await this.eventService.createEvent(event); return { message: 'Event gespeichert' }; } } diff --git a/bizmatch-server/src/file/file.service.ts b/bizmatch-server/src/file/file.service.ts index acb800a..077ec8f 100644 --- a/bizmatch-server/src/file/file.service.ts +++ b/bizmatch-server/src/file/file.service.ts @@ -1,38 +1,22 @@ import { Inject, Injectable } from '@nestjs/common'; -import { readFileSync } from 'fs'; import fs from 'fs-extra'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; -import { join } from 'path'; import sharp from 'sharp'; import { Logger } from 'winston'; -import { ImageProperty, Subscription } from '../models/main.model'; @Injectable() export class FileService { - private subscriptions: any; constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) { - this.loadSubscriptions(); fs.ensureDirSync(`./pictures`); fs.ensureDirSync(`./pictures/profile`); fs.ensureDirSync(`./pictures/logo`); fs.ensureDirSync(`./pictures/property`); } // ############ - // Subscriptions - // ############ - private loadSubscriptions(): void { - const filePath = join(__dirname, '../..', 'assets', 'subscriptions.json'); - const rawData = readFileSync(filePath, 'utf8'); - this.subscriptions = JSON.parse(rawData); - } - getSubscriptions(): Subscription[] { - return this.subscriptions; - } - // ############ // Profile // ############ async storeProfilePicture(file: Express.Multer.File, adjustedEmail: string) { - let quality = 50; + const quality = 50; const output = await sharp(file.buffer) .resize({ width: 300 }) .avif({ quality }) // Verwende AVIF @@ -47,7 +31,7 @@ export class FileService { // Logo // ############ async storeCompanyLogo(file: Express.Multer.File, adjustedEmail: string) { - let quality = 50; + const quality = 50; const output = await sharp(file.buffer) .resize({ width: 300 }) .avif({ quality }) // Verwende AVIF @@ -76,7 +60,6 @@ export class FileService { } } async hasPropertyImages(imagePath: string, serial: string): Promise { - const result: ImageProperty[] = []; const directory = `./pictures/property/${imagePath}/${serial}`; if (fs.existsSync(directory)) { const files = await fs.readdir(directory); @@ -86,7 +69,6 @@ export class FileService { } } async storePropertyPicture(file: Express.Multer.File, imagePath: string, serial: string): Promise { - const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'; const directory = `./pictures/property/${imagePath}/${serial}`; fs.ensureDirSync(`${directory}`); const imageName = await this.getNextImageName(directory); @@ -113,16 +95,15 @@ export class FileService { } } async resizeImageToAVIF(buffer: Buffer, maxSize: number, imageName: string, directory: string) { - let quality = 50; // AVIF kann mit niedrigeren Qualitätsstufen gute Ergebnisse erzielen - let output; - let start = Date.now(); - output = await sharp(buffer) + const quality = 50; // AVIF kann mit niedrigeren Qualitätsstufen gute Ergebnisse erzielen + const start = Date.now(); + const output = await sharp(buffer) .resize({ width: 1500 }) .avif({ quality }) // Verwende AVIF //.webp({ quality }) // Verwende Webp .toBuffer(); await sharp(output).toFile(`${directory}/${imageName}.avif`); // Ersetze Dateierweiterung - let timeTaken = Date.now() - start; + const timeTaken = Date.now() - start; this.logger.info(`Quality: ${quality} - Time: ${timeTaken} milliseconds`); } deleteImage(path: string) { diff --git a/bizmatch-server/src/geo/geo.controller.ts b/bizmatch-server/src/geo/geo.controller.ts index 2997e7e..1dfb2db 100644 --- a/bizmatch-server/src/geo/geo.controller.ts +++ b/bizmatch-server/src/geo/geo.controller.ts @@ -35,7 +35,7 @@ export class GeoController { return this.geoService.findCountiesStartingWith(countyRequest.prefix, countyRequest.states); } @Get('ipinfo/georesult/wysiwyg') - fetchIpAndGeoLocation(@RealIp() ipInfo: RealIpInfo): any { - return this.geoService.fetchIpAndGeoLocation(ipInfo); + async fetchIpAndGeoLocation(@RealIp() ipInfo: RealIpInfo): Promise { + return await this.geoService.fetchIpAndGeoLocation(ipInfo); } } diff --git a/bizmatch-server/src/geo/geo.service.ts b/bizmatch-server/src/geo/geo.service.ts index f7b3fde..039acc1 100644 --- a/bizmatch-server/src/geo/geo.service.ts +++ b/bizmatch-server/src/geo/geo.service.ts @@ -31,7 +31,7 @@ export class GeoService { this.counties.forEach(stateData => { if (!states || states.includes(stateData.state)) { stateData.counties.forEach(county => { - if (county.startsWith(prefix.toUpperCase())) { + if (county.startsWith(prefix?.toUpperCase())) { results.push({ id: idCounter++, name: county, diff --git a/bizmatch-server/src/jwt.strategy.ts b/bizmatch-server/src/jwt.strategy.ts index d06e93f..a768050 100644 --- a/bizmatch-server/src/jwt.strategy.ts +++ b/bizmatch-server/src/jwt.strategy.ts @@ -29,28 +29,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) { // jwksRequestsPerMinute: 5, jwksUri: `https://auth.bizmatch.net/realms/${realm}/protocol/openid-connect/certs`, }), - // cache: true, // Enable caching of validated tokens - // cacheTTL: 60 * 60 * 1000, // 1 hour cache TTL - // secretOrKeyProvider: (request, rawJwtToken, done) => { - // const start = Date.now(); - // const decodedToken = jwt.decode(rawJwtToken, { complete: true }); - // const kid = decodedToken?.header?.kid; - // if (pemCache.has(kid)) { - // this.logger.info(`Using cached PEM for kid ${kid}`); - // return done(null, pemCache.get(kid)); - // } - // const key = staticCerts.keys.find(k => k.kid === kid); - - // if (!key) { - // this.logger.error(`No matching key found for kid: ${kid}`); - // return done(new Error('No matching key found'), null); - // } - - // const publicKey = jwkToPem(key); - // pemCache.set(kid, publicKey); - // done(null, publicKey); - // this.logger.info(`Total JWT verification time: ${Date.now() - start}ms`); - // }, audience: 'account', // Keycloak Client ID authorize: '', issuer: `https://auth.bizmatch.net/realms/${realm}`, @@ -67,7 +45,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) { throw new UnauthorizedException(); } const result = { userId: payload.sub, firstname: payload.given_name, lastname: payload.family_name, username: payload.preferred_username, roles: payload.realm_access?.roles }; - this.logger.info(`JWT User: ${JSON.stringify(result)}`); // Debugging: JWT Payload anzeigen return result; } } diff --git a/bizmatch-server/src/listings/broker-listings.controller.ts b/bizmatch-server/src/listings/broker-listings.controller.ts index 1806fc3..9411ba6 100644 --- a/bizmatch-server/src/listings/broker-listings.controller.ts +++ b/bizmatch-server/src/listings/broker-listings.controller.ts @@ -12,7 +12,7 @@ export class BrokerListingsController { ) {} @Post('search') - find(@Body() criteria: UserListingCriteria): any { - return this.userService.searchUserListings(criteria); + async find(@Body() criteria: UserListingCriteria): Promise { + return await this.userService.searchUserListings(criteria); } } diff --git a/bizmatch-server/src/listings/business-listing.service.ts b/bizmatch-server/src/listings/business-listing.service.ts index 3942ca9..1dbfda6 100644 --- a/bizmatch-server/src/listings/business-listing.service.ts +++ b/bizmatch-server/src/listings/business-listing.service.ts @@ -280,14 +280,4 @@ export class BusinessListingService { }) .where(sql`${businesses.id} = ${id}`); } - // ############################################################## - // States - // ############################################################## - // async getStates(): Promise { - // return await this.conn - // .select({ state: businesses.state, count: sql`count(${businesses.id})`.mapWith(Number) }) - // .from(businesses) - // .groupBy(sql`${businesses.state}`) - // .orderBy(sql`count desc`); - // } } diff --git a/bizmatch-server/src/listings/business-listings.controller.ts b/bizmatch-server/src/listings/business-listings.controller.ts index 8b26e30..ef1e587 100644 --- a/bizmatch-server/src/listings/business-listings.controller.ts +++ b/bizmatch-server/src/listings/business-listings.controller.ts @@ -16,48 +16,46 @@ export class BusinessListingsController { @UseGuards(OptionalJwtAuthGuard) @Get(':id') - findById(@Request() req, @Param('id') id: string): any { - return this.listingsService.findBusinessesById(id, req.user as JwtUser); + async findById(@Request() req, @Param('id') id: string): Promise { + return await this.listingsService.findBusinessesById(id, req.user as JwtUser); } @UseGuards(JwtAuthGuard) @Get('favorites/all') - findFavorites(@Request() req): any { - return this.listingsService.findFavoriteListings(req.user as JwtUser); + async findFavorites(@Request() req): Promise { + return await this.listingsService.findFavoriteListings(req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Get('user/:userid') - findByUserId(@Request() req, @Param('userid') userid: string): Promise { - return this.listingsService.findBusinessesByEmail(userid, req.user as JwtUser); + async findByUserId(@Request() req, @Param('userid') userid: string): Promise { + return await this.listingsService.findBusinessesByEmail(userid, req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Post('find') - find(@Request() req, @Body() criteria: BusinessListingCriteria): any { - return this.listingsService.searchBusinessListings(criteria, req.user as JwtUser); + async find(@Request() req, @Body() criteria: BusinessListingCriteria): Promise { + return await this.listingsService.searchBusinessListings(criteria, req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Post('findTotal') - findTotal(@Request() req, @Body() criteria: BusinessListingCriteria): Promise { - return this.listingsService.getBusinessListingsCount(criteria, req.user as JwtUser); + async findTotal(@Request() req, @Body() criteria: BusinessListingCriteria): Promise { + return await this.listingsService.getBusinessListingsCount(criteria, req.user as JwtUser); } @Post() - create(@Body() listing: any) { - this.logger.info(`Save Listing`); - return this.listingsService.createListing(listing); + async create(@Body() listing: any) { + return await this.listingsService.createListing(listing); } @Put() - update(@Body() listing: any) { - this.logger.info(`Save Listing`); - return this.listingsService.updateBusinessListing(listing.id, listing); + async update(@Body() listing: any) { + return await this.listingsService.updateBusinessListing(listing.id, listing); } @Delete('listing/:id') - deleteById(@Param('id') id: string) { - this.listingsService.deleteListing(id); + async deleteById(@Param('id') id: string) { + await this.listingsService.deleteListing(id); } @UseGuards(JwtAuthGuard) @Delete('favorite/:id') - deleteFavorite(@Request() req, @Param('id') id: string) { - this.listingsService.deleteFavorite(id, req.user as JwtUser); + async deleteFavorite(@Request() req, @Param('id') id: string) { + await this.listingsService.deleteFavorite(id, req.user as JwtUser); } } diff --git a/bizmatch-server/src/listings/commercial-property-listings.controller.ts b/bizmatch-server/src/listings/commercial-property-listings.controller.ts index 8c39d1d..a3cfb46 100644 --- a/bizmatch-server/src/listings/commercial-property-listings.controller.ts +++ b/bizmatch-server/src/listings/commercial-property-listings.controller.ts @@ -18,18 +18,18 @@ export class CommercialPropertyListingsController { @UseGuards(OptionalJwtAuthGuard) @Get(':id') - findById(@Request() req, @Param('id') id: string): any { - return this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser); + async findById(@Request() req, @Param('id') id: string): Promise { + return await this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser); } @UseGuards(JwtAuthGuard) @Get('favorites/all') - findFavorites(@Request() req): any { - return this.listingsService.findFavoriteListings(req.user as JwtUser); + async findFavorites(@Request() req): Promise { + return await this.listingsService.findFavoriteListings(req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Get('user/:email') - findByEmail(@Request() req, @Param('email') email: string): Promise { - return this.listingsService.findCommercialPropertiesByEmail(email, req.user as JwtUser); + async findByEmail(@Request() req, @Param('email') email: string): Promise { + return await this.listingsService.findCommercialPropertiesByEmail(email, req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Post('find') @@ -38,27 +38,25 @@ export class CommercialPropertyListingsController { } @UseGuards(OptionalJwtAuthGuard) @Post('findTotal') - findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise { - return this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser); + async findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise { + return await this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser); } @Post() async create(@Body() listing: any) { - this.logger.info(`Save Listing`); return await this.listingsService.createListing(listing); } @Put() async update(@Body() listing: any) { - this.logger.info(`Save Listing`); return await this.listingsService.updateCommercialPropertyListing(listing.id, listing); } @Delete('listing/:id/:imagePath') - deleteById(@Param('id') id: string, @Param('imagePath') imagePath: string) { - this.listingsService.deleteListing(id); + async deleteById(@Param('id') id: string, @Param('imagePath') imagePath: string) { + await this.listingsService.deleteListing(id); this.fileService.deleteDirectoryIfExists(imagePath); } @UseGuards(JwtAuthGuard) @Delete('favorite/:id') - deleteFavorite(@Request() req, @Param('id') id: string) { - this.listingsService.deleteFavorite(id, req.user as JwtUser); + async deleteFavorite(@Request() req, @Param('id') id: string) { + await this.listingsService.deleteFavorite(id, req.user as JwtUser); } } diff --git a/bizmatch-server/src/mail/mail.controller.ts b/bizmatch-server/src/mail/mail.controller.ts index f4415b1..1b708e4 100644 --- a/bizmatch-server/src/mail/mail.controller.ts +++ b/bizmatch-server/src/mail/mail.controller.ts @@ -7,19 +7,19 @@ import { MailService } from './mail.service'; export class MailController { constructor(private mailService: MailService) {} @Post() - sendEMail(@Body() mailInfo: MailInfo): Promise { + async sendEMail(@Body() mailInfo: MailInfo): Promise { if (mailInfo.listing) { - return this.mailService.sendInquiry(mailInfo); + return await this.mailService.sendInquiry(mailInfo); } else { - return this.mailService.sendRequest(mailInfo); + return await this.mailService.sendRequest(mailInfo); } } @Post('subscriptionConfirmation') - sendSubscriptionConfirmation(@Body() user: User): Promise { - return this.mailService.sendSubscriptionConfirmation(user); + async sendSubscriptionConfirmation(@Body() user: User): Promise { + return await this.mailService.sendSubscriptionConfirmation(user); } @Post('send2Friend') - send2Friend(@Body() shareByEMail: ShareByEMail): Promise { - return this.mailService.send2Friend(shareByEMail); + async send2Friend(@Body() shareByEMail: ShareByEMail): Promise { + return await this.mailService.send2Friend(shareByEMail); } } diff --git a/bizmatch-server/src/main.ts b/bizmatch-server/src/main.ts index 99089f5..e3e3210 100644 --- a/bizmatch-server/src/main.ts +++ b/bizmatch-server/src/main.ts @@ -1,14 +1,20 @@ +import { LoggerService } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import bodyParser from 'body-parser'; import express from 'express'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { AppModule } from './app.module'; async function bootstrap() { const server = express(); server.set('trust proxy', true); const app = await NestFactory.create(AppModule); + // const logger = app.get(WINSTON_MODULE_NEST_PROVIDER); + const logger = app.get(WINSTON_MODULE_NEST_PROVIDER); + app.useLogger(logger); app.use('/bizmatch/payment/webhook', bodyParser.raw({ type: 'application/json' })); app.setGlobalPrefix('bizmatch'); + app.enableCors({ origin: '*', //origin: 'http://localhost:4200', // Die URL Ihrer Angular-App diff --git a/bizmatch-server/src/payment/payment.controller.ts b/bizmatch-server/src/payment/payment.controller.ts index 47ba6a4..8c84b6a 100644 --- a/bizmatch-server/src/payment/payment.controller.ts +++ b/bizmatch-server/src/payment/payment.controller.ts @@ -17,21 +17,21 @@ export class PaymentController { @UseGuards(AdminAuthGuard) @Get('user/all') async getAllStripeCustomer(): Promise { - return this.paymentService.getAllStripeCustomer(); + return await this.paymentService.getAllStripeCustomer(); } @UseGuards(AdminAuthGuard) @Get('subscription/all') async getAllStripeSubscriptions(): Promise { - return this.paymentService.getAllStripeSubscriptions(); + return await this.paymentService.getAllStripeSubscriptions(); } @UseGuards(AdminAuthGuard) @Get('paymentmethod/:email') async getStripePaymentMethods(@Param('email') email: string): Promise { - return this.paymentService.getStripePaymentMethod(email); + return await this.paymentService.getStripePaymentMethod(email); } @Post('create-checkout-session') async createCheckoutSession(@Body() checkout: Checkout) { - return this.paymentService.createCheckoutSession(checkout); + return await this.paymentService.createCheckoutSession(checkout); } @Post('webhook') async handleWebhook(@Req() req: Request, @Res() res: Response): Promise { diff --git a/bizmatch-server/src/payment/payment.service.ts b/bizmatch-server/src/payment/payment.service.ts index fd92e55..660a2b1 100644 --- a/bizmatch-server/src/payment/payment.service.ts +++ b/bizmatch-server/src/payment/payment.service.ts @@ -101,7 +101,6 @@ export class PaymentService { username: keycloakUser.email, roles: [], }); - this.logger.info(JSON.stringify(session)); user.subscriptionId = session.subscription as string; const subscription = await this.stripe.subscriptions.retrieve(user.subscriptionId); user.customerType = 'professional'; diff --git a/bizmatch-server/src/request-duration/request-duration.middleware.ts b/bizmatch-server/src/request-duration/request-duration.middleware.ts index 2f64805..70ba4f5 100644 --- a/bizmatch-server/src/request-duration/request-duration.middleware.ts +++ b/bizmatch-server/src/request-duration/request-duration.middleware.ts @@ -7,19 +7,21 @@ export class RequestDurationMiddleware implements NestMiddleware { 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}`); + res.on('finish', () => { - // const duration = Date.now() - start; - // this.logger.log(`${req.method} ${req.url} - ${duration}ms`); const duration = Date.now() - start; - let logMessage = `${req.method} ${req.url} - ${duration}ms`; + let logMessage = `${req.method} ${req.originalUrl} - ${duration}ms - IP: ${clientIp}`; if (req.method === 'POST' || req.method === 'PUT') { const body = JSON.stringify(req.body); - logMessage += ` - Body: ${body}`; + logMessage += ` - Incoming Body: ${body}`; } this.logger.log(logMessage); }); + next(); } } diff --git a/bizmatch-server/src/user/user.controller.ts b/bizmatch-server/src/user/user.controller.ts index 787de3c..e8cddf1 100644 --- a/bizmatch-server/src/user/user.controller.ts +++ b/bizmatch-server/src/user/user.controller.ts @@ -20,17 +20,13 @@ export class UserController { @UseGuards(OptionalJwtAuthGuard) @Get() async findByMail(@Request() req, @Query('mail') mail: string): Promise { - this.logger.info(`Searching for user with EMail: ${mail}`); const user = await this.userService.getUserByMail(mail, req.user as JwtUser); - this.logger.info(`Found user: ${JSON.stringify(user)}`); return user; } @Get(':id') async findById(@Param('id') id: string): Promise { - this.logger.info(`Searching for user with ID: ${id}`); const user = await this.userService.getUserById(id); - this.logger.info(`Found user: ${JSON.stringify(user)}`); return user; } @UseGuards(AdminAuthGuard) @@ -40,10 +36,8 @@ export class UserController { } @Post() async save(@Body() user: any): Promise { - this.logger.info(`Saving user: ${JSON.stringify(user)}`); try { const savedUser = await this.userService.saveUser(user); - this.logger.info(`User persisted: ${JSON.stringify(savedUser)}`); return savedUser; } catch (error) { if (error instanceof ZodError) { @@ -60,27 +54,23 @@ export class UserController { } @Post('guaranteed') async saveGuaranteed(@Body() user: any): Promise { - this.logger.info(`Saving user guaranteed: ${JSON.stringify(user)}`); const savedUser = await this.userService.saveUser(user, false); - this.logger.info(`User persisted guaranteed: ${JSON.stringify(savedUser)}`); return savedUser; } @Post('search') async find(@Body() criteria: UserListingCriteria): Promise<{ results: User[]; totalCount: number }> { - this.logger.info(`Searching for users with criteria: ${JSON.stringify(criteria)}`); const foundUsers = await this.userService.searchUserListings(criteria); - this.logger.info(`Found users: ${JSON.stringify(foundUsers)}`); return foundUsers; } @Post('findTotal') - findTotal(@Body() criteria: UserListingCriteria): Promise { - return this.userService.getUserListingsCount(criteria); + async findTotal(@Body() criteria: UserListingCriteria): Promise { + return await this.userService.getUserListingsCount(criteria); } @UseGuards(JwtAuthGuard) @Get('subscriptions/:id') async findSubscriptionsById(@Param('id') id: string): Promise { - const subscriptions = this.fileService.getSubscriptions(); + const subscriptions = []; const user = await this.userService.getUserById(id); subscriptions.forEach(s => { s.userId = user.id; diff --git a/bizmatch/src/app/components/header/header.component.ts b/bizmatch/src/app/components/header/header.component.ts index d32ccaa..de4b0c6 100644 --- a/bizmatch/src/app/components/header/header.component.ts +++ b/bizmatch/src/app/components/header/header.component.ts @@ -100,7 +100,7 @@ export class HeaderComponent { this.baseRoute = url.split('/')[1]; // Nimmt den ersten Teil der Route nach dem ersten '/' const specialRoutes = [, '', '']; this.criteria = getCriteriaProxy(this.baseRoute, this); - this.searchService.search(this.criteria); + // this.searchService.search(this.criteria); } setupSortByOptions() { this.sortByOptions = []; diff --git a/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts b/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts index 8d4b566..9bd1271 100644 --- a/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts +++ b/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts @@ -2,6 +2,7 @@ import { CommonModule, NgOptimizedImage } from '@angular/common'; import { ChangeDetectorRef, Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; import { LISTINGS_PER_PAGE, ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; @@ -13,7 +14,7 @@ import { SearchService } from '../../../services/search.service'; import { SelectOptionsService } from '../../../services/select-options.service'; import { UserService } from '../../../services/user.service'; import { getCriteriaStateObject } from '../../../utils/utils'; - +@UntilDestroy() @Component({ selector: 'app-broker-listings', standalone: true, @@ -55,7 +56,7 @@ export class BrokerListingsComponent { ) { this.criteria = getCriteriaStateObject('brokerListings'); this.init(); - this.searchService.currentCriteria.subscribe(criteria => { + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { if (criteria && criteria.criteriaType === 'brokerListings') { this.criteria = criteria as UserListingCriteria; this.search(); diff --git a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts index e6ec5b0..f52a1a7 100644 --- a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts +++ b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import dayjs from 'dayjs'; import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model'; import { BusinessListingCriteria, LISTINGS_PER_PAGE, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; @@ -12,7 +13,7 @@ import { ListingsService } from '../../../services/listings.service'; import { SearchService } from '../../../services/search.service'; import { SelectOptionsService } from '../../../services/select-options.service'; import { getCriteriaStateObject } from '../../../utils/utils'; - +@UntilDestroy() @Component({ selector: 'app-business-listings', standalone: true, @@ -51,7 +52,7 @@ export class BusinessListingsComponent { ) { this.criteria = getCriteriaStateObject('businessListings'); this.init(); - this.searchService.currentCriteria.subscribe(criteria => { + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { if (criteria && criteria.criteriaType === 'businessListings') { this.criteria = criteria as BusinessListingCriteria; this.search(); diff --git a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts index 3610908..dcdc9a0 100644 --- a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts +++ b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import dayjs from 'dayjs'; import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model'; import { CommercialPropertyListingCriteria, LISTINGS_PER_PAGE, ResponseCommercialPropertyListingArray } from '../../../../../../bizmatch-server/src/models/main.model'; @@ -12,7 +13,7 @@ import { ListingsService } from '../../../services/listings.service'; import { SearchService } from '../../../services/search.service'; import { SelectOptionsService } from '../../../services/select-options.service'; import { getCriteriaStateObject } from '../../../utils/utils'; - +@UntilDestroy() @Component({ selector: 'app-commercial-property-listings', standalone: true, @@ -50,7 +51,7 @@ export class CommercialPropertyListingsComponent { ) { this.criteria = getCriteriaStateObject('commercialPropertyListings'); this.init(); - this.searchService.currentCriteria.subscribe(criteria => { + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { if (criteria && criteria.criteriaType === 'commercialPropertyListings') { this.criteria = criteria as CommercialPropertyListingCriteria; this.search();