d9f1422d6f
Korrigiert die deutschen Zeichenketten in beiden Mails aus mail.service.ts (Betreff, Passwort-Reset-Fliesstext, Willkommens-Mail) sowie die domaincheck-Modulbeschreibung in domaincheck.seed.ts auf echte Umlaute. Wortlaut identisch, nur Zeichen korrigiert; die englischen Zweige bleiben unveraendert. seedModule() ist ein upsert mit update-Zweig und laeuft in onModuleInit — ein API-Neustart schreibt die korrigierte Beschreibung ueber bestehende Datenbankzeilen, ohne Migration oder Backfill. pnpm --filter @tessera/api run build: sauber. pnpm --filter @tessera/api run type-check: sauber. pnpm --filter @tessera/api run test: 46 Testdateien, 642 Tests gruen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
135 lines
3.7 KiB
TypeScript
135 lines
3.7 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { MailerService } from '@nestjs-modules/mailer';
|
|
|
|
@Injectable()
|
|
export class MailService {
|
|
private readonly logger = new Logger(MailService.name);
|
|
private readonly appUrl: string;
|
|
|
|
constructor(
|
|
private mailerService: MailerService,
|
|
private configService: ConfigService,
|
|
) {
|
|
this.appUrl = this.configService.get<string>(
|
|
'TESSERA_APP_URL',
|
|
'http://localhost:3000',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Send a password reset email with a time-limited token link.
|
|
* T-02-12: The caller always returns 200 regardless of whether this succeeds
|
|
* (no email enumeration).
|
|
*/
|
|
async sendPasswordResetEmail(
|
|
email: string,
|
|
token: string,
|
|
locale: string = 'de',
|
|
): Promise<void> {
|
|
const resetLink = `${this.appUrl}/reset-password/${token}`;
|
|
|
|
const isGerman = locale === 'de';
|
|
const subject = isGerman
|
|
? 'Passwort zurücksetzen - Tessera'
|
|
: 'Reset your password - Tessera';
|
|
|
|
const text = isGerman
|
|
? [
|
|
'Hallo,',
|
|
'',
|
|
'Sie haben eine Passwortzurücksetzung für Ihren Tessera-Account angefordert.',
|
|
'',
|
|
`Klicken Sie auf den folgenden Link, um Ihr Passwort zurückzusetzen:`,
|
|
resetLink,
|
|
'',
|
|
'Dieser Link ist 1 Stunde gültig und kann nur einmal verwendet werden.',
|
|
'',
|
|
'Falls Sie diese Anfrage nicht gestellt haben, können Sie diese E-Mail ignorieren.',
|
|
'',
|
|
'Mit freundlichen Grüßen,',
|
|
'Ihr Tessera-Team',
|
|
].join('\n')
|
|
: [
|
|
'Hello,',
|
|
'',
|
|
'You have requested a password reset for your Tessera account.',
|
|
'',
|
|
'Click the following link to reset your password:',
|
|
resetLink,
|
|
'',
|
|
'This link is valid for 1 hour and can only be used once.',
|
|
'',
|
|
'If you did not request this, you can safely ignore this email.',
|
|
'',
|
|
'Best regards,',
|
|
'The Tessera Team',
|
|
].join('\n');
|
|
|
|
try {
|
|
await this.mailerService.sendMail({
|
|
to: email,
|
|
subject,
|
|
text,
|
|
});
|
|
this.logger.log(`Password reset email sent to ${email}`);
|
|
} catch (error) {
|
|
// Log but don't throw -- caller returns 200 regardless (T-02-12)
|
|
this.logger.error(
|
|
`Failed to send password reset email to ${email}`,
|
|
error instanceof Error ? error.stack : String(error),
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a welcome email to a newly created user (optional).
|
|
*/
|
|
async sendWelcomeEmail(
|
|
email: string,
|
|
username: string,
|
|
locale: string = 'de',
|
|
): Promise<void> {
|
|
const isGerman = locale === 'de';
|
|
const subject = isGerman
|
|
? 'Willkommen bei Tessera'
|
|
: 'Welcome to Tessera';
|
|
|
|
const text = isGerman
|
|
? [
|
|
`Hallo ${username},`,
|
|
'',
|
|
'Ihr Tessera-Account wurde erstellt.',
|
|
'',
|
|
`Sie können sich unter ${this.appUrl}/login anmelden.`,
|
|
'',
|
|
'Mit freundlichen Grüßen,',
|
|
'Ihr Tessera-Team',
|
|
].join('\n')
|
|
: [
|
|
`Hello ${username},`,
|
|
'',
|
|
'Your Tessera account has been created.',
|
|
'',
|
|
`You can sign in at ${this.appUrl}/login.`,
|
|
'',
|
|
'Best regards,',
|
|
'The Tessera Team',
|
|
].join('\n');
|
|
|
|
try {
|
|
await this.mailerService.sendMail({
|
|
to: email,
|
|
subject,
|
|
text,
|
|
});
|
|
this.logger.log(`Welcome email sent to ${email}`);
|
|
} catch (error) {
|
|
this.logger.error(
|
|
`Failed to send welcome email to ${email}`,
|
|
error instanceof Error ? error.stack : String(error),
|
|
);
|
|
}
|
|
}
|
|
}
|