diff --git a/bizmatch-server/src/app.module.ts b/bizmatch-server/src/app.module.ts index cd9750e..eec86d4 100644 --- a/bizmatch-server/src/app.module.ts +++ b/bizmatch-server/src/app.module.ts @@ -1,7 +1,7 @@ -import { MiddlewareConsumer, Module } from '@nestjs/common'; +import { createParamDecorator, ExecutionContext, MiddlewareConsumer, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { PassportModule } from '@nestjs/passport'; -import { WinstonModule, utilities as nestWinstonModuleUtilities } from 'nest-winston'; +import { utilities as nestWinstonModuleUtilities, WinstonModule } from 'nest-winston'; import * as winston from 'winston'; import { AiModule } from './ai/ai.module'; import { AppController } from './app.controller'; @@ -18,11 +18,17 @@ import dotenvFlow from 'dotenv-flow'; import { EventModule } from './event/event.module'; import { JwtStrategy } from './jwt.strategy'; import { MailModule } from './mail/mail.module'; +import { RealIpInfo } from './models/main.model'; 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'; - +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 }; +}); // function loadEnvFiles() { // // Determine which additional env file to load // let envFilePath = ''; diff --git a/bizmatch-server/src/event/event.controller.ts b/bizmatch-server/src/event/event.controller.ts index e2b256c..487cfe6 100644 --- a/bizmatch-server/src/event/event.controller.ts +++ b/bizmatch-server/src/event/event.controller.ts @@ -1,5 +1,7 @@ -import { Body, Controller, Headers, Ip, Post } from '@nestjs/common'; +import { Body, Controller, Headers, Post } from '@nestjs/common'; +import { RealIp } from 'src/app.module'; import { ListingEvent } from 'src/models/db.model'; +import { RealIpInfo } from 'src/models/main.model'; import { EventService } from './event.service'; @Controller('event') @@ -8,10 +10,10 @@ export class EventController { @Post() async createEvent( @Body() event: ListingEvent, // Struktur des Body-Objekts entsprechend anpassen - @Ip() userIp: string, // IP Adresse des Clients + @RealIp() ipInfo: RealIpInfo, // IP Adresse des Clients @Headers('user-agent') userAgent: string, // User-Agent des Clients ) { - event.userIp = userIp; + event.userIp = ipInfo.ip; event.userAgent = userAgent; await this.eventService.createEvent(event); return { message: 'Event gespeichert' }; diff --git a/bizmatch-server/src/geo/geo.controller.ts b/bizmatch-server/src/geo/geo.controller.ts index 1dfb2db..ad99c8e 100644 --- a/bizmatch-server/src/geo/geo.controller.ts +++ b/bizmatch-server/src/geo/geo.controller.ts @@ -1,17 +1,9 @@ -import { Body, Controller, createParamDecorator, ExecutionContext, Get, Param, Post } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { RealIp } from 'src/app.module'; +import { RealIpInfo } from 'src/models/main.model'; import { CountyRequest } from 'src/models/server.model'; import { GeoService } from './geo.service'; -export interface RealIpInfo { - ip: string; - countryCode?: string; -} -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 }; -}); @Controller('geo') export class GeoController { constructor(private geoService: GeoService) {} diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index 2a9aab4..1899a7c 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -408,3 +408,7 @@ export interface CombinedUser { stripeUser?: StripeUser; stripeSubscription?: StripeSubscription; } +export interface RealIpInfo { + ip: string; + countryCode?: string; +} diff --git a/bizmatch/src/app/app.routes.ts b/bizmatch/src/app/app.routes.ts index 4abd1b0..dbf83a5 100644 --- a/bizmatch/src/app/app.routes.ts +++ b/bizmatch/src/app/app.routes.ts @@ -2,7 +2,6 @@ import { Routes } from '@angular/router'; import { LogoutComponent } from './components/logout/logout.component'; import { NotFoundComponent } from './components/not-found/not-found.component'; -import { PaymentComponent } from './components/payment/payment.component'; import { AuthGuard } from './guards/auth.guard'; import { ListingCategoryGuard } from './guards/listing-category.guard'; import { UserListComponent } from './pages/admin/user-list/user-list.component'; @@ -151,10 +150,6 @@ export const routes: Routes = [ path: 'pricing/:id', component: PricingComponent, }, - { - path: 'payment', - component: PaymentComponent, - }, { path: 'success', component: SuccessComponent, diff --git a/bizmatch/src/app/components/confirmation/confirmation.component.ts b/bizmatch/src/app/components/confirmation/confirmation.component.ts index 84a55a0..04606f0 100644 --- a/bizmatch/src/app/components/confirmation/confirmation.component.ts +++ b/bizmatch/src/app/components/confirmation/confirmation.component.ts @@ -25,8 +25,8 @@ import { ConfirmationService } from './confirmation.service'; @let confirmation = (confirmationService.confirmation$ | async); -

{{ confirmation.message }}

- @if(confirmation.buttons==='both'){ +

{{ confirmation?.message }}

+ @if(confirmation?.buttons==='both'){ - - - -
- -
- - - - - - -
-
-
-
-
Subscription Price
-
$49.00
-
- - - -
-
Tax
-
$1.43
-
-
- -
-
Total
-
$50.43
-
-
- -
- - - - - -
-
- - -

- Payment processed by Stripe for - Bizmatch Inc. - - United States Of America -

- - - diff --git a/bizmatch/src/app/components/payment/payment.component.ts b/bizmatch/src/app/components/payment/payment.component.ts deleted file mode 100644 index 6140f24..0000000 --- a/bizmatch/src/app/components/payment/payment.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, inject, ViewChild } from '@angular/core'; -import { FormsModule, NgForm } from '@angular/forms'; -import { StripeCardCvcElementOptions, StripeCardNumberElementOptions, StripeElementsOptions } from '@stripe/stripe-js'; -import { injectStripe, StripeCardComponent, StripeCardCvcComponent, StripeCardExpiryComponent, StripeCardGroupDirective, StripeCardNumberComponent, StripeElementsDirective } from 'ngx-stripe'; -import { PaymentService } from './payment.service'; - -@Component({ - selector: 'app-payment-modal', - templateUrl: './payment.component.html', - standalone: true, - imports: [CommonModule, FormsModule, StripeCardNumberComponent, StripeCardExpiryComponent, StripeCardCvcComponent, StripeElementsDirective, StripeCardGroupDirective], - styles: ` - .ngx-input{ - padding:0.85rem - } - `, -}) -export class PaymentComponent { - @ViewChild('cardElement') cardElement: StripeCardComponent; - stripe = injectStripe('your-stripe-publishable-key'); - name: string; - constructor() {} - cvcOptions: StripeCardCvcElementOptions = { - style: { - base: { - iconColor: '#666EE8', - color: '#111827', - fontWeight: '300', - fontFamily: 'sans-serif', - fontSize: '14px', - '::placeholder': { - color: '#CFD7E0', - // color: '#6B7280', - }, - }, - }, - }; - numberOptions: StripeCardNumberElementOptions = { - showIcon: true, - style: { - base: { - iconColor: '#666EE8', - color: '#111827', - fontWeight: '300', - fontFamily: 'sans-serif', - fontSize: '14px', - '::placeholder': { - color: '#CFD7E0', - // color: '#6B7280', - }, - }, - }, - }; - - elementsOptions: StripeElementsOptions = { - locale: 'en', - }; - - paymentService = inject(PaymentService); - - onSubmit(form: NgForm) { - if (form.valid) { - this.stripe.createToken(this.cardElement.element, { name: this.name }).subscribe(result => { - if (result.token) { - this.paymentService.processPayment(result.token.id).subscribe(() => { - console.log('Payment successful'); - }); - } else if (result.error) { - console.error(result.error.message); - } - }); - } - } -} diff --git a/bizmatch/src/app/components/payment/payment.service.ts b/bizmatch/src/app/components/payment/payment.service.ts deleted file mode 100644 index ce479b6..0000000 --- a/bizmatch/src/app/components/payment/payment.service.ts +++ /dev/null @@ -1,32 +0,0 @@ -// 1. Shared Service (modal.service.ts) -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { BehaviorSubject, Observable } from 'rxjs'; - -@Injectable({ - providedIn: 'root', -}) -export class PaymentService { - private modalVisibleSubject = new BehaviorSubject(false); - modalVisible$: Observable = this.modalVisibleSubject.asObservable(); - private resolvePromise!: (value: boolean) => void; - constructor(private http: HttpClient) {} - openPaymentModal() { - this.modalVisibleSubject.next(true); - return new Promise(resolve => { - this.resolvePromise = resolve; - }); - } - accept(): void { - this.modalVisibleSubject.next(false); - this.resolvePromise(true); - } - - reject(): void { - this.modalVisibleSubject.next(false); - this.resolvePromise(false); - } - processPayment(token: string): Observable { - return this.http.post('/api/subscription', { token }); - } -} diff --git a/bizmatch/src/app/pages/subscription/account/account.component.ts b/bizmatch/src/app/pages/subscription/account/account.component.ts index c5a30eb..3e88b06 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.ts +++ b/bizmatch/src/app/pages/subscription/account/account.component.ts @@ -234,6 +234,7 @@ export class AccountComponent { this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}`; this.sharedService.changeProfilePhoto(this.profileUrl); } + this.userService.changeUser(this.user); await this.userService.saveGuaranteed(this.user); } } diff --git a/bizmatch/src/app/services/geo.service.ts b/bizmatch/src/app/services/geo.service.ts index d91d75f..3e18e5d 100644 --- a/bizmatch/src/app/services/geo.service.ts +++ b/bizmatch/src/app/services/geo.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { BehaviorSubject, lastValueFrom, Observable } from 'rxjs'; +import { lastValueFrom, Observable } from 'rxjs'; import { CityAndStateResult, CountyResult, GeoResult, IpInfo } from '../../../../bizmatch-server/src/models/main.model'; import { Place } from '../../../../bizmatch-server/src/models/server.model'; import { environment } from '../../environments/environment'; @@ -11,7 +11,6 @@ import { environment } from '../../environments/environment'; export class GeoService { private apiBaseUrl = environment.apiBaseUrl; private baseUrl: string = 'https://nominatim.openstreetmap.org/search'; - private ipInfo$ = new BehaviorSubject(null); private fetchingData: Observable | null = null; private readonly storageKey = 'ipInfo'; constructor(private http: HttpClient) {} @@ -34,27 +33,6 @@ export class GeoService { return this.http.get(`${this.apiBaseUrl}/bizmatch/geo/ipinfo/georesult/wysiwyg`); } - // getIpInfo(): Observable { - // if (this.ipInfo$.getValue() !== null) { - // // Wenn wir bereits Daten haben, geben wir sie sofort zurück - // return this.ipInfo$.asObservable(); - // } else if (this.fetchingData) { - // // Wenn wir gerade Daten abrufen, geben wir diesen Observable zurück - // return this.fetchingData; - // } else { - // // Ansonsten initiieren wir den Abruf - // this.fetchingData = this.fetchIpAndGeoLocation().pipe( - // tap(data => this.ipInfo$.next(data)), - // catchError(error => { - // console.error('Error fetching IP info:', error); - // this.ipInfo$.next(null); - // return of(null); - // }), - // shareReplay(1), - // ); - // return this.fetchingData; - // } - // } async getIpInfo(): Promise { // Versuche zuerst, die Daten aus dem sessionStorage zu holen const storedData = sessionStorage.getItem(this.storageKey); diff --git a/bizmatch/src/app/services/search.service.ts b/bizmatch/src/app/services/search.service.ts index cc0db36..2183cbf 100644 --- a/bizmatch/src/app/services/search.service.ts +++ b/bizmatch/src/app/services/search.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { Subject } from 'rxjs'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; @Injectable({ providedIn: 'root', }) export class SearchService { - private criteriaSource = new BehaviorSubject(null); + private criteriaSource = new Subject(); currentCriteria = this.criteriaSource.asObservable(); constructor() {} diff --git a/bizmatch/src/app/services/shared.service.ts b/bizmatch/src/app/services/shared.service.ts index 0cc0d60..4fc6e1d 100644 --- a/bizmatch/src/app/services/shared.service.ts +++ b/bizmatch/src/app/services/shared.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class SharedService { - private profilePhotoSource = new BehaviorSubject(null); + private profilePhotoSource = new Subject(); currentProfilePhoto = this.profilePhotoSource.asObservable(); constructor() {} diff --git a/bizmatch/src/app/services/user.service.ts b/bizmatch/src/app/services/user.service.ts index 7bf6798..7ced182 100644 --- a/bizmatch/src/app/services/user.service.ts +++ b/bizmatch/src/app/services/user.service.ts @@ -1,7 +1,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { PaymentMethod } from '@stripe/stripe-js'; -import { BehaviorSubject, catchError, forkJoin, lastValueFrom, map, Observable, of } from 'rxjs'; +import { catchError, forkJoin, lastValueFrom, map, Observable, of, Subject } from 'rxjs'; import urlcat from 'urlcat'; import { User } from '../../../../bizmatch-server/src/models/db.model'; import { CombinedUser, KeycloakUser, ResponseUsersArray, StripeSubscription, StripeUser, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; @@ -13,7 +13,7 @@ import { environment } from '../../environments/environment'; export class UserService { private apiBaseUrl = environment.apiBaseUrl; - private userSource = new BehaviorSubject(undefined); + private userSource = new Subject(); currentUser = this.userSource.asObservable(); constructor(private http: HttpClient) {}