diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2022-09-18 03:27:08 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-18 03:27:08 +0900 |
| commit | b75184ec8e3436200bacdcd832e3324702553d20 (patch) | |
| tree | 8b7e316f29e95df921db57289c8b8da476d18f07 /packages/backend/src/core/EmailService.ts | |
| parent | Update ROADMAP.md (diff) | |
| download | sharkey-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.ts | 175 |
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, + }; + } +} |