summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/EmailService.ts
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-09-18 03:27:08 +0900
committerGitHub <noreply@github.com>2022-09-18 03:27:08 +0900
commitb75184ec8e3436200bacdcd832e3324702553d20 (patch)
tree8b7e316f29e95df921db57289c8b8da476d18f07 /packages/backend/src/core/EmailService.ts
parentUpdate ROADMAP.md (diff)
downloadsharkey-b75184ec8e3436200bacdcd832e3324702553d20.tar.gz
sharkey-b75184ec8e3436200bacdcd832e3324702553d20.tar.bz2
sharkey-b75184ec8e3436200bacdcd832e3324702553d20.zip
なんかもうめっちゃ変えた
Diffstat (limited to 'packages/backend/src/core/EmailService.ts')
-rw-r--r--packages/backend/src/core/EmailService.ts175
1 files changed, 175 insertions, 0 deletions
diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
new file mode 100644
index 0000000000..7d6960b73b
--- /dev/null
+++ b/packages/backend/src/core/EmailService.ts
@@ -0,0 +1,175 @@
+import * as nodemailer from 'nodemailer';
+import { Inject, Injectable } from '@nestjs/common';
+import { validate as validateEmail } from 'deep-email-validator';
+import { MetaService } from '@/core/MetaService.js';
+import { DI } from '@/di-symbols.js';
+import { Config } from '@/config.js';
+import Logger from '@/logger.js';
+import { UserProfilesRepository } from '@/models/index.js';
+
+@Injectable()
+export class EmailService {
+ #logger: Logger;
+
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.userProfilesRepository)
+ private userProfilesRepository: UserProfilesRepository,
+
+ private metaService: MetaService,
+ ) {
+ this.#logger = new Logger('email');
+ }
+
+ public async sendEmail(to: string, subject: string, html: string, text: string) {
+ const meta = await this.metaService.fetch(true);
+
+ const iconUrl = `${this.config.url}/static-assets/mi-white.png`;
+ const emailSettingUrl = `${this.config.url}/settings/email`;
+
+ const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';
+
+ const transporter = nodemailer.createTransport({
+ host: meta.smtpHost,
+ port: meta.smtpPort,
+ secure: meta.smtpSecure,
+ ignoreTLS: !enableAuth,
+ proxy: this.config.proxySmtp,
+ auth: enableAuth ? {
+ user: meta.smtpUser,
+ pass: meta.smtpPass,
+ } : undefined,
+ } as any);
+
+ try {
+ // TODO: htmlサニタイズ
+ const info = await transporter.sendMail({
+ from: meta.email!,
+ to: to,
+ subject: subject,
+ text: text,
+ html: `<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>${ subject }</title>
+ <style>
+ html {
+ background: #eee;
+ }
+
+ body {
+ padding: 16px;
+ margin: 0;
+ font-family: sans-serif;
+ font-size: 14px;
+ }
+
+ a {
+ text-decoration: none;
+ color: #86b300;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+
+ main {
+ max-width: 500px;
+ margin: 0 auto;
+ background: #fff;
+ color: #555;
+ }
+ main > header {
+ padding: 32px;
+ background: #86b300;
+ }
+ main > header > img {
+ max-width: 128px;
+ max-height: 28px;
+ vertical-align: bottom;
+ }
+ main > article {
+ padding: 32px;
+ }
+ main > article > h1 {
+ margin: 0 0 1em 0;
+ }
+ main > footer {
+ padding: 32px;
+ border-top: solid 1px #eee;
+ }
+
+ nav {
+ box-sizing: border-box;
+ max-width: 500px;
+ margin: 16px auto 0 auto;
+ padding: 0 32px;
+ }
+ nav > a {
+ color: #888;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <header>
+ <img src="${ meta.logoImageUrl ?? meta.iconUrl ?? iconUrl }"/>
+ </header>
+ <article>
+ <h1>${ subject }</h1>
+ <div>${ html }</div>
+ </article>
+ <footer>
+ <a href="${ emailSettingUrl }">${ 'Email setting' }</a>
+ </footer>
+ </main>
+ <nav>
+ <a href="${ this.config.url }">${ this.config.host }</a>
+ </nav>
+ </body>
+</html>`,
+ });
+
+ this.#logger.info(`Message sent: ${info.messageId}`);
+ } catch (err) {
+ this.#logger.error(err as Error);
+ throw err;
+ }
+ }
+
+ public async validateEmailForAccount(emailAddress: string): Promise<{
+ available: boolean;
+ reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp';
+ }> {
+ const meta = await this.metaService.fetch();
+
+ const exist = await this.userProfilesRepository.countBy({
+ emailVerified: true,
+ email: emailAddress,
+ });
+
+ const validated = meta.enableActiveEmailValidation ? await validateEmail({
+ email: emailAddress,
+ validateRegex: true,
+ validateMx: true,
+ validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
+ validateDisposable: true, // 捨てアドかどうかチェック
+ validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
+ }) : { valid: true };
+
+ const available = exist === 0 && validated.valid;
+
+ return {
+ available,
+ reason: available ? null :
+ exist !== 0 ? 'used' :
+ validated.reason === 'regex' ? 'format' :
+ validated.reason === 'disposable' ? 'disposable' :
+ validated.reason === 'mx' ? 'mx' :
+ validated.reason === 'smtp' ? 'smtp' :
+ null,
+ };
+ }
+}