From 263032b6806ba3d296ba2956716de34c7d42ebe3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 11 Feb 2021 17:47:30 +0900 Subject: [PATCH 01/12] =?UTF-8?q?HTML=E3=83=A1=E3=83=BC=E3=83=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/mi-white.png | Bin 0 -> 18767 bytes src/services/send-email.ts | 84 ++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 assets/mi-white.png diff --git a/assets/mi-white.png b/assets/mi-white.png new file mode 100644 index 0000000000000000000000000000000000000000..1e57da6b38197f95f848fcdcb2308e4e490cae8a GIT binary patch literal 18767 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 literal 0 HcmV?d00001 diff --git a/src/services/send-email.ts b/src/services/send-email.ts index 5a8f92be54..e713ea78d5 100644 --- a/src/services/send-email.ts +++ b/src/services/send-email.ts @@ -8,6 +8,9 @@ export const logger = new Logger('email'); export async function sendEmail(to: string, subject: string, text: string) { const meta = await fetchMeta(true); + const iconUrl = `${config.url}/assets/mi-white.png`; + const emailSettingUrl = `${config.url}/settings/email`; + const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; const transporter = nodemailer.createTransport({ @@ -26,8 +29,85 @@ export async function sendEmail(to: string, subject: string, text: string) { const info = await transporter.sendMail({ from: meta.email!, to: to, - subject: subject || 'Misskey', - text: text + subject: subject, + text: text, + html: ` + + + + ${ subject } + + + +
+
+ +
+
+

${ subject }

+
${ text }
+
+ +
+ + + + ` }); logger.info('Message sent: %s', info.messageId); From ab9343a788aa7c2a3f51a2adcb26780a56906af2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 11 Feb 2021 17:49:28 +0900 Subject: [PATCH 02/12] add note --- src/services/send-email.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/send-email.ts b/src/services/send-email.ts index e713ea78d5..151228c7e3 100644 --- a/src/services/send-email.ts +++ b/src/services/send-email.ts @@ -26,6 +26,7 @@ export async function sendEmail(to: string, subject: string, text: string) { } as any); try { + // TODO: htmlサニタイズ const info = await transporter.sendMail({ from: meta.email!, to: to, From 33bd8fb9d97408a778c38a846e2e44a31fd95c38 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 11 Feb 2021 22:56:58 +0900 Subject: [PATCH 03/12] fix ui --- src/client/pages/settings/plugin.install.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/settings/plugin.install.vue b/src/client/pages/settings/plugin.install.vue index 34c62619ad..0873f28d23 100644 --- a/src/client/pages/settings/plugin.install.vue +++ b/src/client/pages/settings/plugin.install.vue @@ -1,6 +1,6 @@ @@ -50,10 +53,10 @@ import FormButton from '@/components/form/button.vue'; import FormInput from '@/components/form/input.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormSwitch from '@/components/form/switch.vue'; -import FormTuple from '@/components/form/tuple.vue'; +import FormSelect from '@/components/form/select.vue'; import FormBase from '@/components/form/base.vue'; import FormGroup from '@/components/form/group.vue'; -import { host } from '@/config'; +import { host, langs } from '@/config'; import { selectFile } from '@/scripts/select-file'; import * as os from '@/os'; @@ -63,7 +66,7 @@ export default defineComponent({ FormInput, FormTextarea, FormSwitch, - FormTuple, + FormSelect, FormBase, FormGroup, }, @@ -77,9 +80,11 @@ export default defineComponent({ icon: faUser }, host, + langs, name: null, description: null, birthday: null, + lang: null, location: null, fieldName0: null, fieldValue0: null, @@ -104,6 +109,7 @@ export default defineComponent({ this.description = this.$i.description; this.location = this.$i.location; this.birthday = this.$i.birthday; + this.lang = this.$i.lang; this.avatarId = this.$i.avatarId; this.bannerId = this.$i.bannerId; this.isBot = this.$i.isBot; @@ -118,6 +124,15 @@ export default defineComponent({ this.fieldValue2 = this.$i.fields[2] ? this.$i.fields[2].value : null; this.fieldName3 = this.$i.fields[3] ? this.$i.fields[3].name : null; this.fieldValue3 = this.$i.fields[3] ? this.$i.fields[3].value : null; + + this.$watch('name', this.save); + this.$watch('description', this.save); + this.$watch('location', this.save); + this.$watch('birthday', this.save); + this.$watch('lang', this.save); + this.$watch('isBot', this.save); + this.$watch('isCat', this.save); + this.$watch('alwaysMarkNsfw', this.save); }, mounted() { @@ -214,14 +229,15 @@ export default defineComponent({ }); }, - save(notify) { + save() { this.saving = true; - os.api('i/update', { + os.apiWithDialog('i/update', { name: this.name || null, description: this.description || null, location: this.location || null, birthday: this.birthday || null, + lang: this.lang || null, isBot: !!this.isBot, isCat: !!this.isCat, alwaysMarkNsfw: !!this.alwaysMarkNsfw, @@ -231,16 +247,8 @@ export default defineComponent({ this.$i.avatarUrl = i.avatarUrl; this.$i.bannerId = i.bannerId; this.$i.bannerUrl = i.bannerUrl; - - if (notify) { - os.success(); - } }).catch(err => { this.saving = false; - os.dialog({ - type: 'error', - text: err.id - }); }); }, } diff --git a/src/client/sw/sw.ts b/src/client/sw/sw.ts index a18d305ea1..c93fe4926d 100644 --- a/src/client/sw/sw.ts +++ b/src/client/sw/sw.ts @@ -5,7 +5,7 @@ declare var self: ServiceWorkerGlobalScope; import { get, set } from 'idb-keyval'; import composeNotification from '@/sw/compose-notification'; -import { I18n } from '@/scripts/i18n'; +import { I18n } from '../../misc/i18n'; //#region Variables const version = _VERSION_; diff --git a/src/client/scripts/i18n.ts b/src/misc/i18n.ts similarity index 66% rename from src/client/scripts/i18n.ts rename to src/misc/i18n.ts index d535e236bb..4fa398763a 100644 --- a/src/client/scripts/i18n.ts +++ b/src/misc/i18n.ts @@ -1,14 +1,9 @@ -// Notice: Service Workerでも使用します export class I18n> { public locale: T; constructor(locale: T) { this.locale = locale; - if (_DEV_) { - console.log('i18n', this.locale); - } - //#region BIND this.t = this.t.bind(this); //#endregion @@ -20,12 +15,6 @@ export class I18n> { try { let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; - if (_DEV_) { - if (!str.includes('{')) { - console.warn(`i18n: '${key}' has no any arg. so ref prop directly instead of call this method.`); - } - } - if (args) { for (const [k, v] of Object.entries(args)) { str = str.replace(`{${k}}`, v); @@ -33,11 +22,7 @@ export class I18n> { } return str; } catch (e) { - if (_DEV_) { - console.warn(`missing localization '${key}'`); - return `⚠'${key}'⚠`; - } - + console.warn(`missing localization '${key}'`); return key; } } diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index 4fab52868f..3a9043fac6 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -4,6 +4,8 @@ import { User } from './user'; import { Page } from './page'; import { notificationTypes } from '../../types'; +// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも +// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン @Entity() export class UserProfile { @PrimaryColumn(id()) @@ -41,6 +43,11 @@ export class UserProfile { value: string; }[]; + @Column('varchar', { + length: 32, nullable: true, + }) + public lang: string | null; + @Column('varchar', { length: 512, nullable: true, comment: 'Remote URL of the user.' @@ -63,6 +70,11 @@ export class UserProfile { }) public emailVerified: boolean; + @Column('jsonb', { + default: ['follow', 'receiveFollowRequest', 'groupInvited'] + }) + public emailNotificationTypes: string[]; + @Column('varchar', { length: 128, nullable: true, }) diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 88861224a4..a3453b1aa6 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -213,6 +213,7 @@ export class UserRepository extends Repository { description: profile!.description, location: profile!.location, birthday: profile!.birthday, + lang: profile!.lang, fields: profile!.fields, followersCount: user.followersCount, followingCount: user.followingCount, @@ -258,7 +259,8 @@ export class UserRepository extends Repository { hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), integrations: profile!.integrations, mutedWords: profile!.mutedWords, - mutingNotificationTypes: profile?.mutingNotificationTypes, + mutingNotificationTypes: profile!.mutingNotificationTypes, + emailNotificationTypes: profile!.emailNotificationTypes, } : {}), ...(opts.includeSecrets ? { diff --git a/src/server/api/endpoints/admin/send-email.ts b/src/server/api/endpoints/admin/send-email.ts index 9af931ad99..c0e77e1621 100644 --- a/src/server/api/endpoints/admin/send-email.ts +++ b/src/server/api/endpoints/admin/send-email.ts @@ -22,5 +22,5 @@ export const meta = { }; export default define(meta, async (ps) => { - await sendEmail(ps.to, ps.subject, ps.text); + await sendEmail(ps.to, ps.subject, ps.text, ps.text); }); diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index 20d9703320..d3d7bace75 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -72,7 +72,9 @@ export default define(meta, async (ps, user) => { const link = `${config.url}/verify-email/${code}`; - sendEmail(ps.email, 'Email verification', `To verify email, please click this link: ${link}`); + sendEmail(ps.email, 'Email verification', + `To verify email, please click this link:
${link}`, + `To verify email, please click this link: ${link}`); } return iObj; diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index e4c0e8cec9..bf1796924a 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -161,6 +161,10 @@ export const meta = { mutingNotificationTypes: { validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])) }, + + emailNotificationTypes: { + validator: $.optional.arr($.str) + }, }, errors: { @@ -206,7 +210,7 @@ export default define(meta, async (ps, user, token) => { if (ps.name !== undefined) updates.name = ps.name; if (ps.description !== undefined) profileUpdates.description = ps.description; - //if (ps.lang !== undefined) updates.lang = ps.lang; + if (ps.lang !== undefined) profileUpdates.lang = ps.lang; if (ps.location !== undefined) profileUpdates.location = ps.location; if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; @@ -226,6 +230,7 @@ export default define(meta, async (ps, user, token) => { if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; + if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; if (ps.avatarId) { const avatar = await DriveFiles.findOne(ps.avatarId); diff --git a/src/services/create-notification.ts b/src/services/create-notification.ts index 5dddaa5727..6cd116040a 100644 --- a/src/services/create-notification.ts +++ b/src/services/create-notification.ts @@ -4,6 +4,7 @@ import { Notifications, Mutings, UserProfiles } from '../models'; import { genId } from '../misc/gen-id'; import { User } from '../models/entities/user'; import { Notification } from '../models/entities/notification'; +import { sendEmailNotification } from './send-email-notification'; export async function createNotification( notifieeId: User['id'], @@ -38,20 +39,22 @@ export async function createNotification( setTimeout(async () => { const fresh = await Notifications.findOne(notification.id); if (fresh == null) return; // 既に削除されているかもしれない - if (!fresh.isRead) { - //#region ただしミュートしているユーザーからの通知なら無視 - const mutings = await Mutings.find({ - muterId: notifieeId - }); - if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { - return; - } - //#endregion + if (fresh.isRead) return; - publishMainStream(notifieeId, 'unreadNotification', packed); - - pushSw(notifieeId, 'notification', packed); + //#region ただしミュートしているユーザーからの通知なら無視 + const mutings = await Mutings.find({ + muterId: notifieeId + }); + if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { + return; } + //#endregion + + publishMainStream(notifieeId, 'unreadNotification', packed); + + pushSw(notifieeId, 'notification', packed); + if (type === 'follow') sendEmailNotification.follow(notifieeId, data); + if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, data); }, 2000); return notification; diff --git a/src/services/send-email-notification.ts b/src/services/send-email-notification.ts new file mode 100644 index 0000000000..7579d5b674 --- /dev/null +++ b/src/services/send-email-notification.ts @@ -0,0 +1,28 @@ +import { UserProfiles } from '../models'; +import { User } from '../models/entities/user'; +import { sendEmail } from './send-email'; +import * as locales from '../../locales/'; +import { I18n } from '../misc/i18n'; + +// TODO: locale ファイルをクライアント用とサーバー用で分けたい + +async function follow(userId: User['id'], args: {}) { + const userProfile = await UserProfiles.findOneOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + sendEmail(userProfile.email, i18n.t('_email._follow.title'), 'test', 'test'); +} + +async function receiveFollowRequest(userId: User['id'], args: {}) { + const userProfile = await UserProfiles.findOneOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), 'test', 'test'); +} + +export const sendEmailNotification = { + follow, + receiveFollowRequest, +}; diff --git a/src/services/send-email.ts b/src/services/send-email.ts index 151228c7e3..c716b36715 100644 --- a/src/services/send-email.ts +++ b/src/services/send-email.ts @@ -5,7 +5,7 @@ import config from '../config'; export const logger = new Logger('email'); -export async function sendEmail(to: string, subject: string, text: string) { +export async function sendEmail(to: string, subject: string, html: string, text: string) { const meta = await fetchMeta(true); const iconUrl = `${config.url}/assets/mi-white.png`; @@ -44,6 +44,9 @@ export async function sendEmail(to: string, subject: string, text: string) { body { padding: 16px; + margin: 0; + font-family: sans-serif; + font-size: 14px; } a { @@ -67,6 +70,7 @@ export async function sendEmail(to: string, subject: string, text: string) { main > header > img { max-width: 128px; max-height: 28px; + vertical-align: bottom; } main > article { padding: 32px; @@ -97,7 +101,7 @@ export async function sendEmail(to: string, subject: string, text: string) {

${ subject }

-
${ text }
+
${ html }