diff --git a/apps/api/src/password/password.service.ts b/apps/api/src/password/password.service.ts index b8db2d0..77b963a 100644 --- a/apps/api/src/password/password.service.ts +++ b/apps/api/src/password/password.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { BadRequestException, Injectable, ServiceUnavailableException, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { IsNull, Repository } from 'typeorm'; @@ -60,7 +60,7 @@ export class PasswordService { } const token = randomToken(); - await this.resetTokens.save( + const resetToken = await this.resetTokens.save( this.resetTokens.create({ username: user.id, email: user.email, @@ -69,7 +69,24 @@ export class PasswordService { }), ); - await this.mail.sendPasswordResetMail(user.email, token); + try { + await this.mail.sendPasswordResetMail(user.email, token); + } catch (error) { + await this.resetTokens.delete({ id: resetToken.id }).catch(() => undefined); + await this.audit + .record({ + type: 'password.reset_mail_failed', + username: user.id, + ipAddress, + userAgent, + metadata: { error: this.errorMessage(error) }, + }) + .catch(() => undefined); + throw new ServiceUnavailableException( + 'Der Reset-Link konnte nicht versendet werden. Bitte versuche es spaeter erneut.', + ); + } + await this.audit.record({ type: 'password.reset_requested', username: user.id, @@ -105,4 +122,8 @@ export class PasswordService { private get tokenSecret(): string { return this.config.getOrThrow('TOKEN_SECRET'); } + + private errorMessage(error: unknown): string { + return error instanceof Error ? error.message : 'unknown error'; + } } diff --git a/apps/api/src/registration/registration.service.ts b/apps/api/src/registration/registration.service.ts index 338e436..2a1654c 100644 --- a/apps/api/src/registration/registration.service.ts +++ b/apps/api/src/registration/registration.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, ConflictException, Injectable } from '@nestjs/common'; +import { BadRequestException, ConflictException, Injectable, ServiceUnavailableException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { In, IsNull, Repository } from 'typeorm'; @@ -60,7 +60,25 @@ export class RegistrationService { }), ); - await this.mail.sendVerificationMail(registration.email, token); + try { + await this.mail.sendVerificationMail(registration.email, token); + } catch (error) { + await this.emailTokens.delete({ registrationId: registration.id }).catch(() => undefined); + await this.registrations.delete({ id: registration.id }).catch(() => undefined); + await this.audit + .record({ + type: 'registration.verification_mail_failed', + username: registration.username, + ipAddress, + userAgent, + metadata: { error: this.errorMessage(error) }, + }) + .catch(() => undefined); + throw new ServiceUnavailableException( + 'Die Bestaetigungs-E-Mail konnte nicht versendet werden. Bitte versuche es spaeter erneut.', + ); + } + await this.audit.record({ type: 'registration.started', username: registration.username,