first commit

This commit is contained in:
Bastian Wagner
2026-07-31 21:02:47 +02:00
commit 6bea4f766a
512 changed files with 64459 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
export interface MailData<T = never> {
to: string;
data: T;
}

View File

@@ -0,0 +1,43 @@
import * as path from 'path';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MailerOptions, MailerOptionsFactory } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
@Injectable()
export class MailConfigService implements MailerOptionsFactory {
constructor(private configService: ConfigService) {}
createMailerOptions(): MailerOptions {
return {
transport: {
host: this.configService.get('mail.host'),
port: this.configService.get('mail.port'),
ignoreTLS: this.configService.get('mail.ignoreTLS'),
secure: this.configService.get('mail.secure'),
requireTLS: this.configService.get('mail.requireTLS'),
auth: {
user: this.configService.get('mail.user'),
pass: this.configService.get('mail.password'),
},
},
defaults: {
from: `"${this.configService.get(
'mail.defaultName',
)}" <${this.configService.get('mail.defaultEmail')}>`,
},
template: {
dir: path.join(
this.configService.get('app.workingDirectory'),
'src',
'mail',
'mail-templates',
),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
} as MailerOptions;
}
}

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=">
<title>{{title}}</title>
</head>
<body style="margin:0;font-family:arial">
<table style="border:0;width:100%">
<tr style="background:#eeeeee">
<td style="padding:20px;color:#808080;text-align:center;font-size:40px;font-weight:600">
{{app_name}}
</td>
</tr>
<tr>
<td style="padding:20px;color:#808080;font-size:16px;font-weight:100">
{{text1}}<br>
{{text2}} {{app_name}}.<br>
{{text3}}
</td>
</tr>
<tr>
<td style="text-align:center">
<a href="{{url}}"
style="display:inline-block;padding:20px;background:#00838f;text-decoration:none;color:#ffffff">{{actionTitle}}</a>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=">
<title>{{title}}</title>
</head>
<body style="margin:0;font-family:arial">
<table style="border:0;width:100%">
<tr style="background:#eeeeee">
<td style="padding:20px;color:#808080;text-align:center;font-size:40px;font-weight:600">
{{app_name}}
</td>
</tr>
<tr>
<td style="padding:20px;color:#808080;font-size:16px;font-weight:100">
{{text1}}<br>
{{text2}}<br>
{{text3}}
</td>
</tr>
<tr>
<td style="text-align:center">
<a href="{{url}}"
style="display:inline-block;padding:20px;background:#00838f;text-decoration:none;color:#ffffff">{{actionTitle}}</a>
</td>
</tr>
<tr>
<td style="padding:20px;color:#808080;font-size:16px;font-weight:100">
{{text4}}
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MailService } from './mail.service';
@Module({
imports: [ConfigModule],
providers: [MailService],
exports: [MailService],
})
export class MailModule {}

View File

@@ -0,0 +1,62 @@
import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { I18n, I18nRequestScopeService } from 'nestjs-i18n';
import { MailData } from './interfaces/mail-data.interface';
@Injectable()
export class MailService {
constructor(
@I18n()
private i18n: I18nRequestScopeService,
private mailerService: MailerService,
private configService: ConfigService,
) {}
async userSignUp(mailData: MailData<{ hash: string }>) {
return;
await this.mailerService.sendMail({
to: mailData.to,
subject: await this.i18n.t('common.confirmEmail'),
text: `${this.configService.get('app.frontendDomain')}/confirm-email/${
mailData.data.hash
} ${await this.i18n.t('common.confirmEmail')}`,
template: 'activation',
context: {
title: await this.i18n.t('common.confirmEmail'),
url: `${this.configService.get('app.frontendDomain')}/confirm-email/${
mailData.data.hash
}`,
actionTitle: await this.i18n.t('common.confirmEmail'),
app_name: this.configService.get('app.name'),
text1: await this.i18n.t('confirm-email.text1'),
text2: await this.i18n.t('confirm-email.text2'),
text3: await this.i18n.t('confirm-email.text3'),
},
});
}
async forgotPassword(mailData: MailData<{ hash: string }>) {
return;
await this.mailerService.sendMail({
to: mailData.to,
subject: await this.i18n.t('common.resetPassword'),
text: `${this.configService.get('app.frontendDomain')}/password-change/${
mailData.data.hash
} ${await this.i18n.t('common.resetPassword')}`,
template: 'reset-password',
context: {
title: await this.i18n.t('common.resetPassword'),
url: `${this.configService.get('app.frontendDomain')}/password-change/${
mailData.data.hash
}`,
actionTitle: await this.i18n.t('common.resetPassword'),
app_name: this.configService.get('app.name'),
text1: await this.i18n.t('reset-password.text1'),
text2: await this.i18n.t('reset-password.text2'),
text3: await this.i18n.t('reset-password.text3'),
text4: await this.i18n.t('reset-password.text4'),
},
});
}
}