Umstellung auf firebase
This commit is contained in:
parent
f6d1b8623c
commit
521e799bff
|
|
@ -13,7 +13,10 @@
|
|||
"stopOnEntry": false,
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"HOST_NAME": "localhost"
|
||||
"HOST_NAME": "localhost",
|
||||
"FIREBASE_PROJECT_ID": "bizmatch-net",
|
||||
"FIREBASE_CLIENT_EMAIL": "firebase-adminsdk-fbsvc@bizmatch-net.iam.gserviceaccount.com",
|
||||
"FIREBASE_PRIVATE_KEY": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsOlDmhG0zi1zh\nlvobM8yAmLDR3P0F7mHcLyAga2rZm9MnPiGcmkoqRtDnxpZXio36PiyEgdKyhJFK\nP+jPJx1Zo/Ko9vb983oCGcz6MWgRKFXwLT4UJXjwjBdNDe/gcl52c+JJtZJR4bwD\n/bBgkoLzU9lF97pJoQypkSXytyxea6yrS2oEDs7SjW7z9JGFsoxFrt7zbMRb8tIs\nyCWe4I9YSgjSrwOw2uXpdrV0qjDkjx1TokuVJHDH9Vi8XhXDBx9y87Ja0hBoYDE9\nJJRLAa70qHQ9ytfdH/H0kucptC1JkdYGmLQHbohoPDuTU/C85JZvqIitwJ4YEH6Y\nfd+gEe5TAgMBAAECggEALrKDI/WNDFhBn1MJzl1dmhKMguKJ4lVPyF0ot1GYv5bu\nCipg/66f5FWeJ/Hi6qqBM3QvKuBuagPixwCMFbrTzO3UijaoIpQlJTOsrbu+rURE\nBOKnfdvpLkO1v6lDPJaWAUULepPWMAhmK6jZ7V1cTzCRbVSteHBH2CQoZ2Z+C71w\nyvzAIr6JRSg4mYbtHrQCXx9odPCRTdiRvxu5QtihiZGFSXnkTfhDNL1DKff7XHKF\nbOaDPumGtE7ypXr+0qyefg8xeTmXxdI4lPdqxd8XTpLFdMU8nW+/sEjdR40G8ikf\nt6nwyMh01YMMNi88t7ZoDvhpLALb4OqHBhDmyMdOWQKBgQDm5I0cqYX18jypC32G\nUhOdOou6IaZlVDNztZUhFPHPrP0P5Qg1PE5E5YybV7GVNXWiNwI/MPPF0JBce/Ie\ngJoXnuQ9kLh7cNZ432Jhz/Nmhytr6RGxoykAMT1fCuVLsTCfuK4e/aDAgVFJ84gS\nsB3TA62t2hak2MMntKoAQeDwWwKBgQC+9K+MRI/Vj1Xl7jwJ+adRQIvOssVz74ZE\nRYwIDZNRdk/c7c63WVHXASCRZbroGvqJgVfnmtwR6XJTnW3tkYqKUl5W9E+FSVbf\ng4aZs1oaVMA/IirVlRbJ4oCT+nDxPPuJ3ceJ4mBcODO82zXaC6pSFCvkpz9k9lc3\nUPlTLk1baQKBgFMbLqODbSFSeH0MErlXL5InMYXkeMT+IqriT/QhWsw6Yrfm4yZu\nN2nbCdocHWIsZNPnYtql3whzgpKXVlWeSlh4K4TxY0WjHr9RAFNeiyh7PKjRsjmz\nFZ3pG0LrZA7zjyHeUmX7OnIv2bd5fZ/kXkfGiiwKVJ4vG0deYtZG4BUDAoGBAJbI\nFRn4RW8HiHdPv37M8E5bXknvpbRfDTE5jVIKjioD9xnneZQTZmkUjcfhgU2nh+8t\n/+B0ypMmN81IgTXW94MzeSTGM0h22a8SZyVUlrA1/bucWiBeYik1vfubBLWoRqLd\nSaNZ6mbHRis5GPO8xFedb+9UFN2/Gq0mNkl1RUYJAoGBALqTxfdr4MXnG6Nhy22V\nWqui9nsHE5RMIvGYBnnq9Kqt8tUEkxB52YkBilx43q/TY4DRMDOeJk2krEbSN3AO\nguTE6BmZacamrt1HIdSAmJ1RktlVDRgIHXMBkBIumCsTCuXaZ+aEjuLOXJDIsIHZ\nEA9ftLrt1h1u+7QPI+E11Fmx\n-----END PRIVATE KEY-----"
|
||||
}
|
||||
// "preLaunchTask": "Start Stripe Listener"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@
|
|||
"dotenv": "^16.4.5",
|
||||
"dotenv-flow": "^4.1.0",
|
||||
"drizzle-orm": "^0.32.0",
|
||||
"firebase": "^11.3.1",
|
||||
"firebase-admin": "^13.1.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"groq-sdk": "^0.5.0",
|
||||
"handlebars": "^4.7.8",
|
||||
|
|
@ -53,10 +55,6 @@
|
|||
"nodemailer": "^6.9.10",
|
||||
"nodemailer-smtp-transport": "^2.7.4",
|
||||
"openai": "^4.52.6",
|
||||
"passport": "^0.7.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.11.5",
|
||||
"pgvector": "^0.2.0",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Controller, Get, Request, UseGuards } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { AuthService } from './auth/auth.service';
|
||||
import { JwtAuthGuard } from './jwt-auth/jwt-auth.guard';
|
||||
import { AuthGuard } from './jwt-auth/auth.guard';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
|
|
@ -10,7 +10,7 @@ export class AppController {
|
|||
private authService: AuthService,
|
||||
) {}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Get()
|
||||
getHello(@Request() req): string {
|
||||
return req.user;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { utilities as nestWinstonModuleUtilities, WinstonModule } from 'nest-winston';
|
||||
import * as winston from 'winston';
|
||||
import { AiModule } from './ai/ai.module';
|
||||
|
|
@ -16,7 +15,6 @@ 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 { APP_INTERCEPTOR } from '@nestjs/core';
|
||||
|
|
@ -66,7 +64,6 @@ console.log(JSON.stringify(process.env, null, 2));
|
|||
ListingsModule,
|
||||
SelectOptionsModule,
|
||||
ImageModule,
|
||||
PassportModule,
|
||||
AiModule,
|
||||
LogModule,
|
||||
// PaymentModule,
|
||||
|
|
@ -76,7 +73,6 @@ console.log(JSON.stringify(process.env, null, 2));
|
|||
providers: [
|
||||
AppService,
|
||||
FileService,
|
||||
JwtStrategy,
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: UserInterceptor, // Registriere den Interceptor global
|
||||
|
|
|
|||
|
|
@ -1,35 +1,62 @@
|
|||
import { Body, Controller, Get, Param, Put, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from 'src/jwt-auth/jwt-auth.guard';
|
||||
import { Body, Controller, Get, HttpException, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common';
|
||||
import { AuthGuard } from 'src/jwt-auth/auth.guard';
|
||||
import admin from 'src/jwt-auth/firebase-admin';
|
||||
import { KeycloakUser } from 'src/models/main.model';
|
||||
import { AdminAuthGuard } from '../jwt-auth/admin-auth.guard';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Get()
|
||||
async getAccessToken(): Promise<any> {
|
||||
return await this.authService.getAccessToken();
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Get()
|
||||
// async getAccessToken(): Promise<any> {
|
||||
// return await this.authService.getAccessToken();
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Get('user/all')
|
||||
async getUsers(): Promise<any> {
|
||||
return await this.authService.getUsers();
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Get('user/all')
|
||||
// async getUsers(): Promise<any> {
|
||||
// return await this.authService.getUsers();
|
||||
// }
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('users/:userid')
|
||||
async getUser(@Param('userid') userId: string): Promise<any> {
|
||||
return await this.authService.getUser(userId);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Put('users/:userid')
|
||||
async updateKeycloakUser(@Body() keycloakUser: KeycloakUser): Promise<any> {
|
||||
return await this.authService.updateKeycloakUser(keycloakUser);
|
||||
}
|
||||
|
||||
@Post('verify-email')
|
||||
async verifyEmail(@Body('oobCode') oobCode: string, @Body('email') email: string) {
|
||||
if (!oobCode || !email) {
|
||||
throw new HttpException('oobCode and email are required', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
// Schritt 1: Hole den Benutzer anhand der E-Mail-Adresse
|
||||
const userRecord = await admin.auth().getUserByEmail(email);
|
||||
|
||||
if (userRecord.emailVerified) {
|
||||
return { message: 'Email is already verified' };
|
||||
}
|
||||
|
||||
// Schritt 2: Aktualisiere den Benutzerstatus
|
||||
// Hinweis: Wir können den oobCode nicht serverseitig validieren.
|
||||
// Wir nehmen an, dass der oobCode korrekt ist, da er von Firebase generiert wurde.
|
||||
await admin.auth().updateUser(userRecord.uid, {
|
||||
emailVerified: true,
|
||||
});
|
||||
|
||||
return { message: 'Email successfully verified' };
|
||||
} catch (error) {
|
||||
throw new HttpException(error.message || 'Failed to verify email', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Get('user/:userid/lastlogin') //e0811669-c7eb-4e5e-a699-e8334d5c5b01 -> aknuth
|
||||
// getLastLogin(@Param('userid') userId: string): any {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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 { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { ListingEvent } from 'src/models/db.model';
|
||||
import { RealIpInfo } from 'src/models/main.model';
|
||||
import { EventService } from './event.service';
|
||||
|
|
@ -9,7 +9,7 @@ import { EventService } from './event.service';
|
|||
export class EventController {
|
||||
constructor(private eventService: EventService) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
async createEvent(
|
||||
@Body() event: ListingEvent, // Struktur des Body-Objekts entsprechend anpassen
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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 { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { RealIpInfo } from 'src/models/main.model';
|
||||
import { CountyRequest } from 'src/models/server.model';
|
||||
import { GeoService } from './geo.service';
|
||||
|
|
@ -9,31 +9,31 @@ import { GeoService } from './geo.service';
|
|||
export class GeoController {
|
||||
constructor(private geoService: GeoService) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':prefix')
|
||||
findByPrefix(@Param('prefix') prefix: string): any {
|
||||
return this.geoService.findCitiesStartingWith(prefix);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get('citiesandstates/:prefix')
|
||||
findByCitiesAndStatesByPrefix(@Param('prefix') prefix: string): any {
|
||||
return this.geoService.findCitiesAndStatesStartingWith(prefix);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':prefix/:state')
|
||||
findByPrefixAndState(@Param('prefix') prefix: string, @Param('state') state: string): any {
|
||||
return this.geoService.findCitiesStartingWith(prefix, state);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('counties')
|
||||
findByPrefixAndStates(@Body() countyRequest: CountyRequest): any {
|
||||
return this.geoService.findCountiesStartingWith(countyRequest.prefix, countyRequest.states);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get('ipinfo/georesult/wysiwyg')
|
||||
async fetchIpAndGeoLocation(@RealIp() ipInfo: RealIpInfo): Promise<any> {
|
||||
return await this.geoService.fetchIpAndGeoLocation(ipInfo);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Controller, Delete, Inject, Param, Post, UploadedFile, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { JwtAuthGuard } from 'src/jwt-auth/jwt-auth.guard';
|
||||
import { AuthGuard } from 'src/jwt-auth/auth.guard';
|
||||
import { Logger } from 'winston';
|
||||
import { FileService } from '../file/file.service';
|
||||
import { CommercialPropertyService } from '../listings/commercial-property.service';
|
||||
|
|
@ -18,14 +18,14 @@ export class ImageController {
|
|||
// ############
|
||||
// Property
|
||||
// ############
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Post('uploadPropertyPicture/:imagePath/:serial')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('imagePath') imagePath: string, @Param('serial') serial: string) {
|
||||
const imagename = await this.fileService.storePropertyPicture(file, imagePath, serial);
|
||||
await this.listingService.addImage(imagePath, serial, imagename);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete('propertyPicture/:imagePath/:serial/:imagename')
|
||||
async deletePropertyImagesById(@Param('imagePath') imagePath: string, @Param('serial') serial: string, @Param('imagename') imagename: string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/property/${imagePath}/${serial}/${imagename}`);
|
||||
|
|
@ -34,13 +34,13 @@ export class ImageController {
|
|||
// ############
|
||||
// Profile
|
||||
// ############
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Post('uploadProfile/:email')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadProfile(@UploadedFile() file: Express.Multer.File, @Param('email') adjustedEmail: string) {
|
||||
await this.fileService.storeProfilePicture(file, adjustedEmail);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete('profile/:email/')
|
||||
async deleteProfileImagesById(@Param('email') email: string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/profile/${email}.avif`);
|
||||
|
|
@ -48,13 +48,13 @@ export class ImageController {
|
|||
// ############
|
||||
// Logo
|
||||
// ############
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Post('uploadCompanyLogo/:email')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File, @Param('email') adjustedEmail: string) {
|
||||
await this.fileService.storeCompanyLogo(file, adjustedEmail);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete('logo/:email/')
|
||||
async deleteLogoImagesById(@Param('email') adjustedEmail: string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/logo/${adjustedEmail}.avif`);
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class AdminAuthGuard extends AuthGuard('jwt') implements CanActivate {
|
||||
canActivate(context: ExecutionContext) {
|
||||
// Add your custom authentication logic here
|
||||
// for example, call super.logIn(request) to establish a session.
|
||||
return super.canActivate(context);
|
||||
}
|
||||
handleRequest(err, user, info) {
|
||||
// You can throw an exception based on either "info" or "err" arguments
|
||||
if (err || !user || !user.roles.includes('ADMIN')) {
|
||||
throw err || new UnauthorizedException(info);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import admin from './firebase-admin';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request>();
|
||||
const token = this.extractTokenFromHeader(request);
|
||||
|
||||
if (!token) {
|
||||
throw new UnauthorizedException('No token provided');
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = await admin.auth().verifyIdToken(token);
|
||||
request['user'] = decodedToken; // Fügen Sie die Benutzerdaten dem Request-Objekt hinzu
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException('Invalid token');
|
||||
}
|
||||
}
|
||||
|
||||
private extractTokenFromHeader(request: Request): string | undefined {
|
||||
const [type, token] = request.headers['authorization']?.split(' ') ?? [];
|
||||
return type === 'Bearer' ? token : undefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import * as admin from 'firebase-admin';
|
||||
import { ServiceAccount } from 'firebase-admin';
|
||||
|
||||
const serviceAccount: ServiceAccount = {
|
||||
projectId: process.env['FIREBASE_PROJECT_ID'],
|
||||
clientEmail: process.env['FIREBASE_CLIENT_EMAIL'],
|
||||
privateKey: process.env['FIREBASE_PRIVATE_KEY']?.replace(/\\n/g, '\n'), // Ersetzen Sie escaped newlines
|
||||
};
|
||||
|
||||
if (!admin.apps.length) {
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
});
|
||||
}
|
||||
|
||||
export default admin;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') implements CanActivate {
|
||||
canActivate(context: ExecutionContext) {
|
||||
// Add your custom authentication logic here
|
||||
// for example, call super.logIn(request) to establish a session.
|
||||
return super.canActivate(context);
|
||||
}
|
||||
handleRequest(err, user, info) {
|
||||
// You can throw an exception based on either "info" or "err" arguments
|
||||
if (err || !user) {
|
||||
throw err || new UnauthorizedException(info);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import admin from './firebase-admin';
|
||||
|
||||
@Injectable()
|
||||
export class OptionalAuthGuard implements CanActivate {
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request>();
|
||||
const token = this.extractTokenFromHeader(request);
|
||||
|
||||
if (!token) {
|
||||
return true; // Kein Token vorhanden, aber Zugriff erlaubt
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = await admin.auth().verifyIdToken(token);
|
||||
request['user'] = decodedToken; // Benutzerdaten zum Request hinzufügen, wenn Token gültig
|
||||
} catch (error) {
|
||||
// Bei ungültigem Token wird kein Fehler geworfen, sondern einfach kein User gesetzt
|
||||
request['user'] = null;
|
||||
}
|
||||
|
||||
return true; // Zugriff wird immer erlaubt
|
||||
}
|
||||
|
||||
private extractTokenFromHeader(request: Request): string | undefined {
|
||||
const [type, token] = request.headers['authorization']?.split(' ') ?? [];
|
||||
return type === 'Bearer' ? token : undefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class OptionalJwtAuthGuard extends AuthGuard('jwt') {
|
||||
handleRequest(err, user, info) {
|
||||
// Wenn der Benutzer nicht authentifiziert ist, aber kein Fehler vorliegt, geben Sie null zurück
|
||||
if (err || !user) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
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 { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { UserListingCriteria } from 'src/models/main.model';
|
||||
import { Logger } from 'winston';
|
||||
import { UserService } from '../user/user.service';
|
||||
|
|
@ -12,7 +12,7 @@ export class BrokerListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('search')
|
||||
async find(@Body() criteria: UserListingCriteria): Promise<any> {
|
||||
return await this.userService.searchUserListings(criteria);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGuards } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { AuthGuard } from 'src/jwt-auth/auth.guard';
|
||||
import { Logger } from 'winston';
|
||||
import { JwtAuthGuard } from '../jwt-auth/jwt-auth.guard';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard';
|
||||
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { BusinessListing } from '../models/db.model';
|
||||
import { BusinessListingCriteria, JwtUser } from '../models/main.model';
|
||||
import { BusinessListingService } from './business-listing.service';
|
||||
|
|
@ -14,52 +15,52 @@ export class BusinessListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':id')
|
||||
async findById(@Request() req, @Param('id') id: string): Promise<any> {
|
||||
return await this.listingsService.findBusinessesById(id, req.user as JwtUser);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('favorites/all')
|
||||
async findFavorites(@Request() req): Promise<any> {
|
||||
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
|
||||
}
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get('user/:userid')
|
||||
async findByUserId(@Request() req, @Param('userid') userid: string): Promise<BusinessListing[]> {
|
||||
return await this.listingsService.findBusinessesByEmail(userid, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('find')
|
||||
async find(@Request() req, @Body() criteria: BusinessListingCriteria): Promise<any> {
|
||||
return await this.listingsService.searchBusinessListings(criteria, req.user as JwtUser);
|
||||
}
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('findTotal')
|
||||
async findTotal(@Request() req, @Body() criteria: BusinessListingCriteria): Promise<number> {
|
||||
return await this.listingsService.getBusinessListingsCount(criteria, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
async create(@Body() listing: any) {
|
||||
return await this.listingsService.createListing(listing);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Put()
|
||||
async update(@Body() listing: any) {
|
||||
return await this.listingsService.updateBusinessListing(listing.id, listing);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Delete('listing/:id')
|
||||
async deleteById(@Param('id') id: string) {
|
||||
await this.listingsService.deleteListing(id);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete('favorite/:id')
|
||||
async deleteFavorite(@Request() req, @Param('id') id: string) {
|
||||
await this.listingsService.deleteFavorite(id, req.user as JwtUser);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGu
|
|||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
import { FileService } from '../file/file.service';
|
||||
import { JwtAuthGuard } from '../jwt-auth/jwt-auth.guard';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard';
|
||||
|
||||
import { AuthGuard } from 'src/jwt-auth/auth.guard';
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { CommercialPropertyListing } from '../models/db.model';
|
||||
import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model';
|
||||
import { CommercialPropertyService } from './commercial-property.service';
|
||||
|
|
@ -16,54 +17,54 @@ export class CommercialPropertyListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':id')
|
||||
async findById(@Request() req, @Param('id') id: string): Promise<any> {
|
||||
return await this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('favorites/all')
|
||||
async findFavorites(@Request() req): Promise<any> {
|
||||
return await this.listingsService.findFavoriteListings(req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@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)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('find')
|
||||
async find(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<any> {
|
||||
return await this.listingsService.searchCommercialProperties(criteria, req.user as JwtUser);
|
||||
}
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('findTotal')
|
||||
async findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<number> {
|
||||
return await this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
async create(@Body() listing: any) {
|
||||
return await this.listingsService.createListing(listing);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Put()
|
||||
async update(@Body() listing: any) {
|
||||
return await this.listingsService.updateCommercialPropertyListing(listing.id, listing);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Delete('listing/:id/:imagePath')
|
||||
async deleteById(@Param('id') id: string, @Param('imagePath') imagePath: string) {
|
||||
await this.listingsService.deleteListing(id);
|
||||
this.fileService.deleteDirectoryIfExists(imagePath);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete('favorite/:id')
|
||||
async deleteFavorite(@Request() req, @Param('id') id: string) {
|
||||
await this.listingsService.deleteFavorite(id, req.user as JwtUser);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Controller, Get, Inject, Param, Request, UseGuards } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { Logger } from 'winston';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard';
|
||||
import { BusinessListingService } from './business-listing.service';
|
||||
import { CommercialPropertyService } from './commercial-property.service';
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ export class UnknownListingsController {
|
|||
private readonly propertyListingsService: CommercialPropertyService,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':id')
|
||||
async findById(@Request() req, @Param('id') id: string): Promise<any> {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { Body, Controller, Inject, Post, Request, UseGuards } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { Logger } from 'winston';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard';
|
||||
import { LogMessage } from '../models/main.model';
|
||||
@Controller('log')
|
||||
export class LogController {
|
||||
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
log(@Request() req, @Body() message: LogMessage) {
|
||||
if (message.severity === 'info') {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
|
||||
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
|
||||
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { ShareByEMail, User } from 'src/models/db.model';
|
||||
import { ErrorResponse, MailInfo } from '../models/main.model';
|
||||
import { MailService } from './mail.service';
|
||||
|
|
@ -8,7 +9,7 @@ import { MailService } from './mail.service';
|
|||
export class MailController {
|
||||
constructor(private mailService: MailService) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
async sendEMail(@Body() mailInfo: MailInfo): Promise<void | ErrorResponse> {
|
||||
if (mailInfo.listing) {
|
||||
|
|
@ -18,13 +19,13 @@ export class MailController {
|
|||
}
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('subscriptionConfirmation')
|
||||
async sendSubscriptionConfirmation(@Body() user: User): Promise<void | ErrorResponse> {
|
||||
return await this.mailService.sendSubscriptionConfirmation(user);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('send2Friend')
|
||||
async send2Friend(@Body() shareByEMail: ShareByEMail): Promise<void | ErrorResponse> {
|
||||
return await this.mailService.send2Friend(shareByEMail);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Body, Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Param, Post, Req, Res, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Get, 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 { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { Checkout } from 'src/models/main.model';
|
||||
import Stripe from 'stripe';
|
||||
import { PaymentService } from './payment.service';
|
||||
|
|
@ -15,25 +14,25 @@ export class PaymentController {
|
|||
// return this.paymentService.createSubscription(subscriptionData);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Get('user/all')
|
||||
async getAllStripeCustomer(): Promise<Stripe.Customer[]> {
|
||||
return await this.paymentService.getAllStripeCustomer();
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Get('user/all')
|
||||
// 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('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(AdminAuthGuard)
|
||||
// @Get('paymentmethod/:email')
|
||||
// async getStripePaymentMethods(@Param('email') email: string): Promise<Stripe.PaymentMethod[]> {
|
||||
// return await this.paymentService.getStripePaymentMethod(email);
|
||||
// }
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('create-checkout-session')
|
||||
async createCheckoutSession(@Body() checkout: Checkout) {
|
||||
return await this.paymentService.createCheckoutSession(checkout);
|
||||
|
|
@ -59,7 +58,7 @@ export class PaymentController {
|
|||
}
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get('subscriptions/:email')
|
||||
async findSubscriptionsById(@Param('email') email: string): Promise<any> {
|
||||
return await this.paymentService.getSubscription(email);
|
||||
|
|
@ -68,10 +67,10 @@ export class PaymentController {
|
|||
* Endpoint zum Löschen eines Stripe-Kunden.
|
||||
* Beispiel: DELETE /stripe/customer/cus_12345
|
||||
*/
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Delete('customer/:id')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
async deleteCustomer(@Param('id') customerId: string): Promise<void> {
|
||||
await this.paymentService.deleteCustomerCompletely(customerId);
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Delete('customer/:id')
|
||||
// @HttpCode(HttpStatus.NO_CONTENT)
|
||||
// async deleteCustomer(@Param('id') customerId: string): Promise<void> {
|
||||
// await this.paymentService.deleteCustomerCompletely(customerId);
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { Controller, Get, UseGuards } from '@nestjs/common';
|
||||
import { OptionalJwtAuthGuard } from 'src/jwt-auth/optional-jwt-auth.guard';
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { SelectOptionsService } from './select-options.service';
|
||||
|
||||
@Controller('select-options')
|
||||
export class SelectOptionsController {
|
||||
constructor(private selectOptionsService: SelectOptionsService) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get()
|
||||
getSelectOption(): any {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { BadRequestException, Body, Controller, Get, Inject, Param, Post, Query, Request, UseGuards } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { AdminAuthGuard } from 'src/jwt-auth/admin-auth.guard';
|
||||
import { Logger } from 'winston';
|
||||
import { ZodError } from 'zod';
|
||||
import { FileService } from '../file/file.service';
|
||||
import { JwtAuthGuard } from '../jwt-auth/jwt-auth.guard';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard';
|
||||
|
||||
import { AuthGuard } from 'src/jwt-auth/auth.guard';
|
||||
import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard';
|
||||
import { User } from '../models/db.model';
|
||||
import { JwtUser, Subscription, UserListingCriteria } from '../models/main.model';
|
||||
import { UserService } from './user.service';
|
||||
|
|
@ -18,26 +18,26 @@ export class UserController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get()
|
||||
async findByMail(@Request() req, @Query('mail') mail: string): Promise<User> {
|
||||
const user = await this.userService.getUserByMail(mail, req.user as JwtUser);
|
||||
return user;
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Get(':id')
|
||||
async findById(@Param('id') id: string): Promise<User> {
|
||||
const user = await this.userService.getUserById(id);
|
||||
return user;
|
||||
}
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Get('user/all')
|
||||
async getAllUser(): Promise<User[]> {
|
||||
return await this.userService.getAllUser();
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Get('user/all')
|
||||
// async getAllUser(): Promise<User[]> {
|
||||
// return await this.userService.getAllUser();
|
||||
// }
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post()
|
||||
async save(@Body() user: any): Promise<User> {
|
||||
try {
|
||||
|
|
@ -57,27 +57,27 @@ export class UserController {
|
|||
}
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('guaranteed')
|
||||
async saveGuaranteed(@Body() user: any): Promise<User> {
|
||||
const savedUser = await this.userService.saveUser(user, false);
|
||||
return savedUser;
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('search')
|
||||
async find(@Body() criteria: UserListingCriteria): Promise<{ results: User[]; totalCount: number }> {
|
||||
const foundUsers = await this.userService.searchUserListings(criteria);
|
||||
return foundUsers;
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@UseGuards(OptionalAuthGuard)
|
||||
@Post('findTotal')
|
||||
async findTotal(@Body() criteria: UserListingCriteria): Promise<number> {
|
||||
return await this.userService.getUserListingsCount(criteria);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('subscriptions/:id')
|
||||
async findSubscriptionsById(@Param('id') id: string): Promise<Subscription[]> {
|
||||
const subscriptions = [];
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export class UserService {
|
|||
.from(schema.users)
|
||||
.where(sql`email = ${email}`)) as User[];
|
||||
if (users.length === 0) {
|
||||
const user: User = { id: undefined, customerType: 'buyer', ...createDefaultUser(email, jwtuser.firstname, jwtuser.lastname, null) };
|
||||
const user: User = { id: undefined, customerType: 'professional', ...createDefaultUser(email, jwtuser.firstname ? jwtuser.firstname : '', jwtuser.lastname ? jwtuser.lastname : '', null) };
|
||||
const u = await this.saveUser(user, false);
|
||||
return u;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@
|
|||
"@angular/router": "^18.1.3",
|
||||
"@bluehalo/ngx-leaflet": "^18.0.2",
|
||||
"@fortawesome/angular-fontawesome": "^0.15.0",
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@ng-select/ng-select": "^13.4.1",
|
||||
"@ngneat/until-destroy": "^10.0.0",
|
||||
"@stripe/stripe-js": "^4.3.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"/api": {
|
||||
"/bizmatch": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!-- <div class="container"> -->
|
||||
<div class="flex flex-col" [ngClass]="{ 'bg-slate-100 print:bg-white': actualRoute !== 'home' }">
|
||||
@if (actualRoute !=='home' && actualRoute !=='login'){
|
||||
<div class="wrapper" [ngClass]="{ 'bg-slate-100 print:bg-white': actualRoute !== 'home' }">
|
||||
@if (actualRoute !=='home' && actualRoute !=='login' && actualRoute!=='emailVerification' && actualRoute!=='email-authorized'){
|
||||
<header></header>
|
||||
}
|
||||
<main class="flex-grow">
|
||||
<main class="flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Routes } from '@angular/router';
|
|||
import { LogoutComponent } from './components/logout/logout.component';
|
||||
import { NotFoundComponent } from './components/not-found/not-found.component';
|
||||
|
||||
import { EmailAuthorizedComponent } from './components/email-authorized/email-authorized.component';
|
||||
import { EmailVerificationComponent } from './components/email-verification/email-verification.component';
|
||||
import { LoginRegisterComponent } from './components/login-register/login-register.component';
|
||||
import { AuthGuard } from './guards/auth.guard';
|
||||
import { ListingCategoryGuard } from './guards/listing-category.guard';
|
||||
|
|
@ -147,6 +149,14 @@ export const routes: Routes = [
|
|||
path: 'pricing',
|
||||
component: PricingComponent,
|
||||
},
|
||||
{
|
||||
path: 'emailVerification',
|
||||
component: EmailVerificationComponent,
|
||||
},
|
||||
{
|
||||
path: 'email-authorized',
|
||||
component: EmailAuthorizedComponent,
|
||||
},
|
||||
{
|
||||
path: 'pricingOverview',
|
||||
component: PricingComponent,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<div class="container mx-auto p-4 text-center min-h-screen bg-gray-100">
|
||||
<ng-container *ngIf="verificationStatus === 'pending'">
|
||||
<p class="text-lg text-gray-600">Verifying your email...</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="verificationStatus === 'success'">
|
||||
<h2 class="text-2xl font-bold text-green-600 mb-5">Your email has been verified</h2>
|
||||
<!-- <p class="text-gray-700 mb-4">You can now sign in with your new account</p> -->
|
||||
<a routerLink="/account" class="inline-block px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors">Follow this link to access your Account Page </a>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="verificationStatus === 'error'">
|
||||
<h2 class="text-2xl font-bold text-red-600 mb-2">Verification failed</h2>
|
||||
<p class="text-gray-700">{{ errorMessage }}</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-authorized',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterModule],
|
||||
templateUrl: './email-authorized.component.html',
|
||||
})
|
||||
export class EmailAuthorizedComponent implements OnInit {
|
||||
verificationStatus: 'pending' | 'success' | 'error' = 'pending';
|
||||
errorMessage: string | null = null;
|
||||
|
||||
constructor(private route: ActivatedRoute, private http: HttpClient, private authService: AuthService, private userService: UserService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const oobCode = this.route.snapshot.queryParamMap.get('oobCode');
|
||||
const email = this.route.snapshot.queryParamMap.get('email');
|
||||
const mode = this.route.snapshot.queryParamMap.get('mode');
|
||||
|
||||
if (mode === 'verifyEmail' && oobCode && email) {
|
||||
this.verifyEmail(oobCode, email);
|
||||
} else {
|
||||
this.verificationStatus = 'error';
|
||||
this.errorMessage = 'Invalid verification link';
|
||||
}
|
||||
}
|
||||
|
||||
private verifyEmail(oobCode: string, email: string): void {
|
||||
this.http.post(`${environment.apiBaseUrl}/bizmatch/auth/verify-email`, { oobCode, email }).subscribe({
|
||||
next: async () => {
|
||||
this.verificationStatus = 'success';
|
||||
await this.authService.refreshToken();
|
||||
const user = await this.userService.getByMail(email);
|
||||
},
|
||||
error: err => {
|
||||
this.verificationStatus = 'error';
|
||||
this.errorMessage = err.error?.message || 'Verification failed';
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
|
||||
<div class="bg-white p-8 rounded shadow-md w-full max-w-md">
|
||||
<h2 class="text-2xl font-bold mb-6 text-center">
|
||||
{{ isLoginMode ? 'Login' : 'Registrierung' }}
|
||||
<div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
|
||||
<h2 class="text-2xl font-bold mb-6 text-center text-gray-800">
|
||||
{{ isLoginMode ? 'Login' : 'Sign Up' }}
|
||||
</h2>
|
||||
|
||||
<!-- Toggle Switch mit Flowbite -->
|
||||
|
|
@ -18,34 +18,99 @@
|
|||
|
||||
<!-- E-Mail Eingabe -->
|
||||
<div class="mb-4">
|
||||
<label for="email" class="block text-gray-700 mb-2">EMail</label>
|
||||
<input id="email" type="email" [(ngModel)]="email" placeholder="Please enter EMail Address" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
|
||||
<label for="email" class="block text-gray-700 mb-2 font-medium">E-Mail</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
[(ngModel)]="email"
|
||||
placeholder="Please enter E-Mail Address"
|
||||
class="w-full px-3 py-2 pl-10 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<fa-icon [icon]="envelope" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Passwort Eingabe -->
|
||||
<div class="mb-4">
|
||||
<label for="password" class="block text-gray-700 mb-2">Password</label>
|
||||
<input id="password" type="password" [(ngModel)]="password" placeholder="Please enter Passwort" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
|
||||
<label for="password" class="block text-gray-700 mb-2 font-medium">Password</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
[(ngModel)]="password"
|
||||
placeholder="Please enter Password"
|
||||
class="w-full px-3 py-2 pl-10 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Passwort-Bestätigung nur im Registrierungsmodus -->
|
||||
<div *ngIf="!isLoginMode" class="mb-6">
|
||||
<label for="confirmPassword" class="block text-gray-700 mb-2">Confirm Password</label>
|
||||
<input id="confirmPassword" type="password" [(ngModel)]="confirmPassword" placeholder="Repeat Password" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
|
||||
<label for="confirmPassword" class="block text-gray-700 mb-2 font-medium">Confirm Password</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
[(ngModel)]="confirmPassword"
|
||||
placeholder="Repeat Password"
|
||||
class="w-full px-3 py-2 pl-10 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="errorMessage" class="text-red-500 text-center mb-4">
|
||||
|
||||
<!-- Fehlermeldung -->
|
||||
<div *ngIf="errorMessage" class="text-red-500 text-center mb-4 text-sm">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
<button (click)="onSubmit()" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 rounded-lg mb-4">
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button (click)="onSubmit()" class="w-full flex items-center justify-center bg-blue-600 hover:bg-blue-700 text-white py-2.5 rounded-lg mb-4 transition-colors duration-200">
|
||||
<!-- <fa-icon [icon]="isLoginMode ? 'fas fas-user-plus' : 'userplus'" class="mr-2"></fa-icon> -->
|
||||
<i *ngIf="isLoginMode" class="fa-solid fa-user-plus mr-2"></i>
|
||||
<i *ngIf="!isLoginMode" class="fa-solid fa-arrow-right mr-2"></i>
|
||||
{{ isLoginMode ? 'Sign in with Email' : 'Register' }}
|
||||
</button>
|
||||
|
||||
<!-- Trennlinie -->
|
||||
<div class="flex items-center justify-center my-4">
|
||||
<span class="border-b w-1/5 md:w-1/4"></span>
|
||||
<span class="text-xs text-center text-gray-500 uppercase mx-2">or</span>
|
||||
<span class="border-b w-1/5 md:w-1/4"></span>
|
||||
<span class="border-b w-1/5 md:w-1/4 border-gray-300"></span>
|
||||
<span class="text-xs text-gray-500 uppercase mx-2">or</span>
|
||||
<span class="border-b w-1/5 md:w-1/4 border-gray-300"></span>
|
||||
</div>
|
||||
|
||||
<button (click)="loginWithGoogle()" class="w-full bg-red-500 hover:bg-red-600 text-white py-2 rounded-lg">Continue with Google</button>
|
||||
<!-- Google Button -->
|
||||
<button (click)="loginWithGoogle()" class="w-full flex items-center justify-center bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 py-2.5 rounded-lg transition-colors duration-200">
|
||||
<!-- <svg class="h-5 w-5 mr-2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12.24 10.32V13.8H15.48C15.336 14.688 14.568 16.368 12.24 16.368C10.224 16.368 8.568 14.688 8.568 12.672C8.568 10.656 10.224 8.976 12.24 8.976C13.32 8.976 14.16 9.432 14.688 10.08L16.704 8.208C15.528 7.032 14.04 6.384 12.24 6.384C8.832 6.384 6 9.216 6 12.672C6 16.128 8.832 18.96 12.24 18.96C15.696 18.96 18.12 16.656 18.12 12.672C18.12 11.952 18.024 11.28 17.88 10.656L12.24 10.32Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg> -->
|
||||
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<path
|
||||
fill="#FFC107"
|
||||
d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"
|
||||
/>
|
||||
<path fill="#FF3D00" d="M6.306 14.691l6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z" />
|
||||
<path fill="#4CAF50" d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238A11.91 11.91 0 0124 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z" />
|
||||
<path fill="#1976D2" d="M43.611 20.083H42V20H24v8h11.303a12.04 12.04 0 01-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z" />
|
||||
</svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
<!-- <button (click)="loginWithGoogle()" class="bg-white text-blue-600 px-6 py-3 rounded-lg shadow-lg hover:bg-gray-100 transition duration-300 flex items-center justify-center">
|
||||
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<path
|
||||
fill="#FFC107"
|
||||
d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"
|
||||
/>
|
||||
<path fill="#FF3D00" d="M6.306 14.691l6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z" />
|
||||
<path fill="#4CAF50" d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238A11.91 11.91 0 0124 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z" />
|
||||
<path fill="#1976D2" d="M43.611 20.083H42V20H24v8h11.303a12.04 12.04 0 01-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z" />
|
||||
</svg>
|
||||
Continue with Google
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import { CommonModule } from '@angular/common';
|
|||
import { Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { faArrowRight, faEnvelope, faLock, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
|
||||
import { LoadingService } from '../../services/loading.service';
|
||||
@Component({
|
||||
selector: 'app-login-register',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, FontAwesomeModule],
|
||||
templateUrl: './login-register.component.html',
|
||||
})
|
||||
export class LoginRegisterComponent {
|
||||
|
|
@ -16,8 +18,11 @@ export class LoginRegisterComponent {
|
|||
confirmPassword: string = '';
|
||||
isLoginMode: boolean = true; // true: Login, false: Registration
|
||||
errorMessage: string = '';
|
||||
|
||||
constructor(private authService: AuthService, private route: ActivatedRoute, private router: Router) {}
|
||||
envelope = faEnvelope;
|
||||
lock = faLock;
|
||||
arrowRight = faArrowRight;
|
||||
userplus = faUserPlus;
|
||||
constructor(private authService: AuthService, private route: ActivatedRoute, private router: Router, private loadingService: LoadingService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Set mode based on query parameter "mode"
|
||||
|
|
@ -53,12 +58,16 @@ export class LoginRegisterComponent {
|
|||
this.errorMessage = 'Passwords do not match.';
|
||||
return;
|
||||
}
|
||||
this.loadingService.startLoading('googleAuth');
|
||||
this.authService
|
||||
.registerWithEmail(this.email, this.password)
|
||||
.then(userCredential => {
|
||||
console.log('Successfully registered:', userCredential);
|
||||
this.loadingService.stopLoading('googleAuth');
|
||||
this.router.navigate(['emailVerification']);
|
||||
})
|
||||
.catch(error => {
|
||||
this.loadingService.stopLoading('googleAuth');
|
||||
console.error('Error during registration:', error);
|
||||
if (error.code === 'auth/email-already-in-use') {
|
||||
this.errorMessage = 'This email address is already in use. Please try logging in.';
|
||||
|
|
@ -76,6 +85,7 @@ export class LoginRegisterComponent {
|
|||
.loginWithGoogle()
|
||||
.then(userCredential => {
|
||||
console.log('Successfully logged in with Google:', userCredential);
|
||||
this.router.navigate([`home`]);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during Google login:', error);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// auth.interceptor.ts
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -10,6 +10,15 @@ export class AuthInterceptor implements HttpInterceptor {
|
|||
constructor(private authService: AuthService) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// Prüfe, ob die Anfrage an die apiBaseUrl gerichtet ist
|
||||
const isApiRequest = req.url.startsWith(environment.apiBaseUrl);
|
||||
|
||||
if (!isApiRequest) {
|
||||
// Wenn es keine API-Anfrage ist, leite die Anfrage unverändert weiter
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
// Wenn es eine API-Anfrage ist, füge den Token hinzu (falls vorhanden)
|
||||
return from(this.authService.getToken()).pipe(
|
||||
switchMap(token => {
|
||||
if (token) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<header class="w-full flex justify-between items-center p-4 bg-white fixed top-0 z-10 h-16 md:h-20">
|
||||
<header class="w-full flex justify-between items-center p-4 bg-white top-0 z-10 h-16 md:h-20">
|
||||
<img src="assets/images/header-logo.png" alt="Logo" class="h-8 md:h-10" />
|
||||
<div class="hidden md:flex items-center space-x-4">
|
||||
@if(user){
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<main class="flex flex-col items-center justify-center mt-16 md:mt-20 lg:px-4 w-full flex-grow">
|
||||
<main class="flex flex-col items-center justify-center lg:px-4 w-full flex-grow">
|
||||
<div class="bg-cover-custom py-20 md:py-40 flex flex-col w-full">
|
||||
<div class="flex justify-center w-full">
|
||||
<div class="w-11/12 md:w-2/3 lg:w-1/2">
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="mt-8 max-lg:hidden">
|
||||
<!-- <div class="mt-8 max-lg:hidden">
|
||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<div class="inline-block min-w-full">
|
||||
|
|
@ -290,8 +290,8 @@
|
|||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if(user.subscriptionPlan==='free'){
|
||||
</div> -->
|
||||
<!-- @if(user.subscriptionPlan==='free'){
|
||||
<div class="flex justify-start">
|
||||
<button
|
||||
routerLink="/pricing"
|
||||
|
|
@ -300,7 +300,7 @@
|
|||
Upgrade Subscription Plan
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
} -->
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { ImageCropperComponent } from 'ngx-image-cropper';
|
|||
import { QuillModule } from 'ngx-quill';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { AutoCompleteCompleteEvent, Invoice, StripeSubscription, UploadParams, ValidationMessage, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { AutoCompleteCompleteEvent, Invoice, UploadParams, ValidationMessage, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
||||
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
||||
|
|
@ -31,10 +31,9 @@ import { ImageService } from '../../../services/image.service';
|
|||
import { LoadingService } from '../../../services/loading.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { SubscriptionsService } from '../../../services/subscriptions.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { checkAndUpdate, isAdmin, map2User } from '../../../utils/utils';
|
||||
import { isAdmin, map2User } from '../../../utils/utils';
|
||||
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
|
|
@ -79,7 +78,7 @@ export class AccountComponent {
|
|||
customerSubTypeOptions: Array<{ value: string; label: string }> = [];
|
||||
tooltipTargetAreasServed = 'tooltip-areasServed';
|
||||
tooltipTargetLicensed = 'tooltip-licensedIn';
|
||||
subscriptions: StripeSubscription[] | any[];
|
||||
// subscriptions: StripeSubscription[] | any[];
|
||||
constructor(
|
||||
public userService: UserService,
|
||||
private geoService: GeoService,
|
||||
|
|
@ -94,7 +93,7 @@ export class AccountComponent {
|
|||
private sharedService: SharedService,
|
||||
private titleCasePipe: TitleCasePipe,
|
||||
private validationMessagesService: ValidationMessagesService,
|
||||
private subscriptionService: SubscriptionsService,
|
||||
// private subscriptionService: SubscriptionsService,
|
||||
private datePipe: DatePipe,
|
||||
private router: Router,
|
||||
private authService: AuthService,
|
||||
|
|
@ -112,57 +111,57 @@ export class AccountComponent {
|
|||
this.user = await this.userService.getByMail(email);
|
||||
}
|
||||
|
||||
this.subscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions(this.user.email));
|
||||
await this.synchronizeSubscriptions(this.subscriptions);
|
||||
// this.subscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions(this.user.email));
|
||||
// await this.synchronizeSubscriptions(this.subscriptions);
|
||||
this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo ? `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
|
||||
this.customerTypeOptions = this.selectOptions.customerTypes
|
||||
.filter(ct => ct.value === 'buyer' || ct.value === 'seller' || this.user.customerType === 'professional')
|
||||
// .filter(ct => ct.value === 'buyer' || ct.value === 'seller' || this.user.customerType === 'professional')
|
||||
.map(type => ({
|
||||
value: type.value,
|
||||
label: this.titleCasePipe.transform(type.name),
|
||||
}));
|
||||
|
||||
this.customerSubTypeOptions = this.selectOptions.customerSubTypes
|
||||
.filter(ct => ct.value !== 'broker' || this.user.customerSubType === 'broker')
|
||||
// .filter(ct => ct.value !== 'broker' || this.user.customerSubType === 'broker')
|
||||
.map(type => ({
|
||||
value: type.value,
|
||||
label: this.titleCasePipe.transform(type.name),
|
||||
}));
|
||||
}
|
||||
async synchronizeSubscriptions(subscriptions: StripeSubscription[]) {
|
||||
let changed = false;
|
||||
if (this.isAdmin()) {
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions.length === 0) {
|
||||
if (!this.user.subscriptionPlan) {
|
||||
this.router.navigate(['pricing']);
|
||||
} else {
|
||||
this.subscriptions = [{ ended_at: null, start_date: Math.floor(new Date(this.user.created).getTime() / 1000), status: null, metadata: { plan: 'Free Plan' } }];
|
||||
changed = checkAndUpdate(changed, this.user.customerType !== 'buyer' && this.user.customerType !== 'seller', () => (this.user.customerType = 'buyer'));
|
||||
changed = checkAndUpdate(changed, !!this.user.customerSubType, () => (this.user.customerSubType = null));
|
||||
changed = checkAndUpdate(changed, this.user.subscriptionPlan !== 'free', () => (this.user.subscriptionPlan = 'free'));
|
||||
changed = checkAndUpdate(changed, !!this.user.subscriptionId, () => (this.user.subscriptionId = null));
|
||||
}
|
||||
} else {
|
||||
const subscription = subscriptions[0];
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerSubType !== 'broker', () => (this.user.customerSubType = 'broker'));
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.subscriptionPlan !== 'broker', () => (this.user.subscriptionPlan = 'broker'));
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && !this.user.subscriptionId, () => (this.user.subscriptionId = subscription.id));
|
||||
// async synchronizeSubscriptions(subscriptions: StripeSubscription[]) {
|
||||
// let changed = false;
|
||||
// if (this.isAdmin()) {
|
||||
// return;
|
||||
// }
|
||||
// if (this.subscriptions.length === 0) {
|
||||
// if (!this.user.subscriptionPlan) {
|
||||
// this.router.navigate(['pricing']);
|
||||
// } else {
|
||||
// this.subscriptions = [{ ended_at: null, start_date: Math.floor(new Date(this.user.created).getTime() / 1000), status: null, metadata: { plan: 'Free Plan' } }];
|
||||
// changed = checkAndUpdate(changed, this.user.customerType !== 'buyer' && this.user.customerType !== 'seller', () => (this.user.customerType = 'buyer'));
|
||||
// changed = checkAndUpdate(changed, !!this.user.customerSubType, () => (this.user.customerSubType = null));
|
||||
// changed = checkAndUpdate(changed, this.user.subscriptionPlan !== 'free', () => (this.user.subscriptionPlan = 'free'));
|
||||
// changed = checkAndUpdate(changed, !!this.user.subscriptionId, () => (this.user.subscriptionId = null));
|
||||
// }
|
||||
// } else {
|
||||
// const subscription = subscriptions[0];
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.customerSubType !== 'broker', () => (this.user.customerSubType = 'broker'));
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && this.user.subscriptionPlan !== 'broker', () => (this.user.subscriptionPlan = 'broker'));
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Broker Plan' && !this.user.subscriptionId, () => (this.user.subscriptionId = subscription.id));
|
||||
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionPlan !== 'professional', () => (this.user.subscriptionPlan = 'professional'));
|
||||
changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionId !== 'professional', () => (this.user.subscriptionId = subscription.id));
|
||||
}
|
||||
if (changed) {
|
||||
await this.userService.saveGuaranteed(this.user);
|
||||
this.cdref.detectChanges();
|
||||
this.cdref.markForCheck();
|
||||
}
|
||||
}
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.customerType !== 'professional', () => (this.user.customerType = 'professional'));
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionPlan !== 'professional', () => (this.user.subscriptionPlan = 'professional'));
|
||||
// changed = checkAndUpdate(changed, subscription.metadata['plan'] === 'Professional Plan' && this.user.subscriptionId !== 'professional', () => (this.user.subscriptionId = subscription.id));
|
||||
// }
|
||||
// if (changed) {
|
||||
// await this.userService.saveGuaranteed(this.user);
|
||||
// this.cdref.detectChanges();
|
||||
// this.cdref.markForCheck();
|
||||
// }
|
||||
// }
|
||||
|
||||
ngOnDestroy() {
|
||||
this.validationMessagesService.clearMessages(); // Löschen Sie alle bestehenden Validierungsnachrichten
|
||||
|
|
@ -273,19 +272,19 @@ export class AccountComponent {
|
|||
this.user.areasServed[index].county = null;
|
||||
}
|
||||
}
|
||||
getLevel(i: number) {
|
||||
return this.subscriptions[i].metadata.plan;
|
||||
}
|
||||
getStartDate(i: number) {
|
||||
return this.datePipe.transform(new Date(this.subscriptions[i].start_date * 1000));
|
||||
}
|
||||
getEndDate(i: number) {
|
||||
return this.subscriptions[i].status === 'trialing' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
}
|
||||
getNextSettlement(i: number) {
|
||||
return this.subscriptions[i].status === 'active' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
}
|
||||
getStatus(i: number) {
|
||||
return this.subscriptions[i].status ? this.subscriptions[i].status : '';
|
||||
}
|
||||
// getLevel(i: number) {
|
||||
// return this.subscriptions[i].metadata.plan;
|
||||
// }
|
||||
// getStartDate(i: number) {
|
||||
// return this.datePipe.transform(new Date(this.subscriptions[i].start_date * 1000));
|
||||
// }
|
||||
// getEndDate(i: number) {
|
||||
// return this.subscriptions[i].status === 'trialing' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
// }
|
||||
// getNextSettlement(i: number) {
|
||||
// return this.subscriptions[i].status === 'active' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
// }
|
||||
// getStatus(i: number) {
|
||||
// return this.subscriptions[i].status ? this.subscriptions[i].status : '';
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,16 @@ export class AuthService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private isEMailVerified(token: string): boolean {
|
||||
try {
|
||||
const payloadBase64 = token.split('.')[1];
|
||||
const payloadJson = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
|
||||
const payload = JSON.parse(payloadJson);
|
||||
return payload.email_verified;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Versucht, mit dem RefreshToken einen neuen Access Token zu erhalten
|
||||
async refreshToken(): Promise<string | null> {
|
||||
const storedRefreshToken = localStorage.getItem('refreshToken');
|
||||
|
|
@ -122,7 +131,9 @@ export class AuthService {
|
|||
*/
|
||||
async getToken(): Promise<string | null> {
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token && this.isTokenValid(token)) {
|
||||
if (token && !this.isEMailVerified(token)) {
|
||||
return null;
|
||||
} else if (token && this.isTokenValid(token) && this.isEMailVerified(token)) {
|
||||
return token;
|
||||
} else {
|
||||
return await this.refreshToken();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export const hostname = window.location.hostname;
|
||||
export const environment_base = {
|
||||
// apiBaseUrl: 'http://localhost:3000',
|
||||
apiBaseUrl: `http://${hostname}:3000`,
|
||||
apiBaseUrl: `http://${hostname}:4200`,
|
||||
imageBaseUrl: 'https://dev.bizmatch.net',
|
||||
buildVersion: '<BUILD_VERSION>',
|
||||
mailinfoUrl: 'https://dev.bizmatch.net',
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@
|
|||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<base href="/" />
|
||||
<link rel="icon" href="assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet" /> -->
|
||||
<!-- <link rel="icon" href="cropped-Favicon-192x192.png" sizes="192x192"> -->
|
||||
<!-- <link rel="apple-touch-icon" href="cropped-Favicon-180x180.png"> -->
|
||||
</head>
|
||||
<body>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -56,6 +56,24 @@ textarea {
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 64px; /* Feste Höhe */
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 0 auto; /* Füllt den verfügbaren Platz */
|
||||
}
|
||||
|
||||
footer {
|
||||
flex-shrink: 0; /* Verhindert Schrumpfen */
|
||||
}
|
||||
|
||||
*:focus,
|
||||
.p-focus {
|
||||
box-shadow: none !important;
|
||||
|
|
|
|||
Loading…
Reference in New Issue