Implement email config
This commit is contained in:
parent
0ce64f8c33
commit
1bc109b42c
|
@ -503,6 +503,10 @@ common/views/components/profile-editor.vue:
|
||||||
saved: "プロフィールを保存しました"
|
saved: "プロフィールを保存しました"
|
||||||
uploading: "アップロード中"
|
uploading: "アップロード中"
|
||||||
upload-failed: "アップロードに失敗しました"
|
upload-failed: "アップロードに失敗しました"
|
||||||
|
email: "メール設定"
|
||||||
|
email-address: "メールアドレス"
|
||||||
|
email-verified: "メールアドレスが確認されました"
|
||||||
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
|
||||||
common/views/widgets/broadcast.vue:
|
common/views/widgets/broadcast.vue:
|
||||||
fetching: "確認中"
|
fetching: "確認中"
|
||||||
|
@ -1123,6 +1127,15 @@ admin/views/instance.vue:
|
||||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||||
external-user-recommendation-timeout: "タイムアウト"
|
external-user-recommendation-timeout: "タイムアウト"
|
||||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||||
|
email-config: "メールサーバーの設定"
|
||||||
|
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||||
|
enable-email: "メール配信を有効にする"
|
||||||
|
email: "メールアドレス"
|
||||||
|
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||||
|
smtp-host: "SMTPホスト"
|
||||||
|
smtp-port: "SMTPポート"
|
||||||
|
smtp-user: "SMTPユーザー"
|
||||||
|
smtp-pass: "SMTPパスワード"
|
||||||
|
|
||||||
admin/views/charts.vue:
|
admin/views/charts.vue:
|
||||||
title: "チャート"
|
title: "チャート"
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
"@types/mongodb": "3.1.14",
|
"@types/mongodb": "3.1.14",
|
||||||
"@types/ms": "0.7.30",
|
"@types/ms": "0.7.30",
|
||||||
"@types/node": "10.12.10",
|
"@types/node": "10.12.10",
|
||||||
|
"@types/nodemailer": "4.6.5",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
"@types/parsimmon": "1.10.0",
|
"@types/parsimmon": "1.10.0",
|
||||||
"@types/portscanner": "2.1.0",
|
"@types/portscanner": "2.1.0",
|
||||||
|
@ -166,6 +167,7 @@
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.1",
|
||||||
"nan": "2.11.1",
|
"nan": "2.11.1",
|
||||||
"nested-property": "0.0.7",
|
"nested-property": "0.0.7",
|
||||||
|
"nodemailer": "4.7.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"object-assign-deep": "0.4.0",
|
"object-assign-deep": "0.4.0",
|
||||||
"on-build-webpack": "0.1.0",
|
"on-build-webpack": "0.1.0",
|
||||||
|
|
|
@ -12,11 +12,15 @@
|
||||||
<section class="fit-bottom">
|
<section class="fit-bottom">
|
||||||
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
|
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
|
||||||
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
|
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
|
||||||
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>{{ $t('maintainer-email') }}</ui-input>
|
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="farEnvelope"/></i>{{ $t('maintainer-email') }}</ui-input>
|
||||||
</section>
|
</section>
|
||||||
<section class="fit-top fit-bottom">
|
<section class="fit-top fit-bottom">
|
||||||
<ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
|
<ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
||||||
|
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
|
||||||
|
</section>
|
||||||
<section class="fit-bottom">
|
<section class="fit-bottom">
|
||||||
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
|
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
|
||||||
<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<span slot="desc">{{ $t('cache-remote-files-desc') }}</span></ui-switch>
|
<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<span slot="desc">{{ $t('cache-remote-files-desc') }}</span></ui-switch>
|
||||||
|
@ -37,10 +41,18 @@
|
||||||
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
|
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
<header><fa :icon="farEnvelope"/> {{ $t('email-config') }}</header>
|
||||||
</section>
|
<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<span slot="desc">{{ $t('email-config-info') }}</span></ui-switch>
|
||||||
<section>
|
<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input>
|
||||||
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
|
<ui-horizon-group inputs>
|
||||||
|
<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input>
|
||||||
|
<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-input v-model="smtpUser" :disabled="!enableEmail">{{ $t('smtp-user') }}</ui-input>
|
||||||
|
<ui-input v-model="smtpPass" :disabled="!enableEmail">{{ $t('smtp-pass') }}</ui-input>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-use-ssl') }}</ui-switch>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<header>summaly Proxy</header>
|
<header>summaly Proxy</header>
|
||||||
|
@ -106,6 +118,7 @@ import i18n from '../../i18n';
|
||||||
import { url, host } from '../../config';
|
import { url, host } from '../../config';
|
||||||
import { toUnicode } from 'punycode';
|
import { toUnicode } from 'punycode';
|
||||||
import { faHeadset, faShieldAlt, faGhost, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
import { faHeadset, faShieldAlt, faGhost, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { faEnvelope as farEnvelope } from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('admin/views/instance.vue'),
|
i18n: i18n('admin/views/instance.vue'),
|
||||||
|
@ -144,7 +157,14 @@ export default Vue.extend({
|
||||||
externalUserRecommendationEngine: null,
|
externalUserRecommendationEngine: null,
|
||||||
externalUserRecommendationTimeout: null,
|
externalUserRecommendationTimeout: null,
|
||||||
summalyProxy: null,
|
summalyProxy: null,
|
||||||
faHeadset, faShieldAlt, faGhost, faUserPlus
|
enableEmail: false,
|
||||||
|
email: null,
|
||||||
|
smtpSecure: false,
|
||||||
|
smtpHost: null,
|
||||||
|
smtpPort: null,
|
||||||
|
smtpUser: null,
|
||||||
|
smtpPass: null,
|
||||||
|
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -177,6 +197,13 @@ export default Vue.extend({
|
||||||
this.externalUserRecommendationEngine = meta.externalUserRecommendationEngine;
|
this.externalUserRecommendationEngine = meta.externalUserRecommendationEngine;
|
||||||
this.externalUserRecommendationTimeout = meta.externalUserRecommendationTimeout;
|
this.externalUserRecommendationTimeout = meta.externalUserRecommendationTimeout;
|
||||||
this.summalyProxy = meta.summalyProxy;
|
this.summalyProxy = meta.summalyProxy;
|
||||||
|
this.enableEmail = meta.enableEmail;
|
||||||
|
this.email = meta.email;
|
||||||
|
this.smtpSecure = meta.smtpSecure;
|
||||||
|
this.smtpHost = meta.smtpHost;
|
||||||
|
this.smtpPort = meta.smtpPort;
|
||||||
|
this.smtpUser = meta.smtpUser;
|
||||||
|
this.smtpPass = meta.smtpPass;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -222,7 +249,14 @@ export default Vue.extend({
|
||||||
enableExternalUserRecommendation: this.enableExternalUserRecommendation,
|
enableExternalUserRecommendation: this.enableExternalUserRecommendation,
|
||||||
externalUserRecommendationEngine: this.externalUserRecommendationEngine,
|
externalUserRecommendationEngine: this.externalUserRecommendationEngine,
|
||||||
externalUserRecommendationTimeout: parseInt(this.externalUserRecommendationTimeout, 10),
|
externalUserRecommendationTimeout: parseInt(this.externalUserRecommendationTimeout, 10),
|
||||||
summalyProxy: this.summalyProxy
|
summalyProxy: this.summalyProxy,
|
||||||
|
enableEmail: this.enableEmail,
|
||||||
|
email: this.email,
|
||||||
|
smtpSecure: this.smtpSecure,
|
||||||
|
smtpHost: this.smtpHost,
|
||||||
|
smtpPort: parseInt(this.smtpPort, 10),
|
||||||
|
smtpUser: this.smtpUser,
|
||||||
|
smtpPass: this.smtpPass
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$root.alert({
|
this.$root.alert({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|
|
@ -66,6 +66,19 @@
|
||||||
<ui-switch v-model="carefulBot" @change="save(false)">{{ $t('careful-bot') }}</ui-switch>
|
<ui-switch v-model="carefulBot" @change="save(false)">{{ $t('careful-bot') }}</ui-switch>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<header>{{ $t('email') }}</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<template v-if="$store.state.i.email != null">
|
||||||
|
<ui-info v-if="$store.state.i.emailVerified">{{ $t('email-verified') }}</ui-info>
|
||||||
|
<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
|
||||||
|
</template>
|
||||||
|
<ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input>
|
||||||
|
<ui-button @click="updateEmail()">{{ $t('save') }}</ui-button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -77,9 +90,11 @@ import { toUnicode } from 'punycode';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/profile-editor.vue'),
|
i18n: i18n('common/views/components/profile-editor.vue'),
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
host: toUnicode(host),
|
host: toUnicode(host),
|
||||||
|
email: null,
|
||||||
name: null,
|
name: null,
|
||||||
username: null,
|
username: null,
|
||||||
location: null,
|
location: null,
|
||||||
|
@ -113,7 +128,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.name = this.$store.state.i.name || '';
|
this.email = this.$store.state.i.email;
|
||||||
|
this.name = this.$store.state.i.name;
|
||||||
this.username = this.$store.state.i.username;
|
this.username = this.$store.state.i.username;
|
||||||
this.location = this.$store.state.i.profile.location;
|
this.location = this.$store.state.i.profile.location;
|
||||||
this.description = this.$store.state.i.description;
|
this.description = this.$store.state.i.description;
|
||||||
|
@ -199,6 +215,12 @@ export default Vue.extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateEmail() {
|
||||||
|
this.$root.api('i/update_email', {
|
||||||
|
email: this.email == '' ? null : this.email
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -214,4 +214,12 @@ export type IMeta = {
|
||||||
enableExternalUserRecommendation?: boolean;
|
enableExternalUserRecommendation?: boolean;
|
||||||
externalUserRecommendationEngine?: string;
|
externalUserRecommendationEngine?: string;
|
||||||
externalUserRecommendationTimeout?: number;
|
externalUserRecommendationTimeout?: number;
|
||||||
|
|
||||||
|
enableEmail?: boolean;
|
||||||
|
email?: string;
|
||||||
|
smtpSecure?: boolean;
|
||||||
|
smtpHost?: string;
|
||||||
|
smtpPort?: number;
|
||||||
|
smtpUser?: string;
|
||||||
|
smtpPass?: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,6 +78,8 @@ export interface ILocalUser extends IUserBase {
|
||||||
host: null;
|
host: null;
|
||||||
keypair: string;
|
keypair: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
emailVerified?: boolean;
|
||||||
|
emailVerifyCode?: string;
|
||||||
password: string;
|
password: string;
|
||||||
token: string;
|
token: string;
|
||||||
twitter: {
|
twitter: {
|
||||||
|
@ -99,9 +101,6 @@ export interface ILocalUser extends IUserBase {
|
||||||
username: string;
|
username: string;
|
||||||
discriminator: string;
|
discriminator: string;
|
||||||
};
|
};
|
||||||
line: {
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
profile: {
|
profile: {
|
||||||
location: string;
|
location: string;
|
||||||
birthday: string; // 'YYYY-MM-DD'
|
birthday: string; // 'YYYY-MM-DD'
|
||||||
|
@ -286,6 +285,7 @@ export const pack = (
|
||||||
delete _user._id;
|
delete _user._id;
|
||||||
|
|
||||||
delete _user.usernameLower;
|
delete _user.usernameLower;
|
||||||
|
delete _user.emailVerifyCode;
|
||||||
|
|
||||||
if (_user.host == null) {
|
if (_user.host == null) {
|
||||||
// Remove private properties
|
// Remove private properties
|
||||||
|
@ -306,11 +306,11 @@ export const pack = (
|
||||||
delete _user.discord.refreshToken;
|
delete _user.discord.refreshToken;
|
||||||
delete _user.discord.expiresDate;
|
delete _user.discord.expiresDate;
|
||||||
}
|
}
|
||||||
delete _user.line;
|
|
||||||
|
|
||||||
// Visible via only the official client
|
// Visible via only the official client
|
||||||
if (!opts.includeSecrets) {
|
if (!opts.includeSecrets) {
|
||||||
delete _user.email;
|
delete _user.email;
|
||||||
|
delete _user.emailVerified;
|
||||||
delete _user.settings;
|
delete _user.settings;
|
||||||
delete _user.clientSettings;
|
delete _user.clientSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,56 @@ export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)'
|
'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
enableEmail: {
|
||||||
|
validator: $.bool.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'メール配信を有効にするか否か'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
email: {
|
||||||
|
validator: $.str.optional.nullable,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'メール配信する際に利用するメールアドレス'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpSecure: {
|
||||||
|
validator: $.bool.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'SMTPサーバがSSLを使用しているか否か'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpHost: {
|
||||||
|
validator: $.str.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'SMTPサーバのホスト'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpPort: {
|
||||||
|
validator: $.num.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'SMTPサーバのポート'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpUser: {
|
||||||
|
validator: $.str.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'SMTPサーバのユーザー名'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpPass: {
|
||||||
|
validator: $.str.optional,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'SMTPサーバのパスワード'
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -359,6 +408,34 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||||
set.externalUserRecommendationTimeout = ps.externalUserRecommendationTimeout;
|
set.externalUserRecommendationTimeout = ps.externalUserRecommendationTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.enableEmail !== undefined) {
|
||||||
|
set.enableEmail = ps.enableEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.email !== undefined) {
|
||||||
|
set.email = ps.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.smtpSecure !== undefined) {
|
||||||
|
set.smtpSecure = ps.smtpSecure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.smtpHost !== undefined) {
|
||||||
|
set.smtpHost = ps.smtpHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.smtpPort !== undefined) {
|
||||||
|
set.smtpPort = ps.smtpPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.smtpUser !== undefined) {
|
||||||
|
set.smtpUser = ps.smtpUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.smtpPass !== undefined) {
|
||||||
|
set.smtpPass = ps.smtpPass;
|
||||||
|
}
|
||||||
|
|
||||||
await Meta.update({}, {
|
await Meta.update({}, {
|
||||||
$set: set
|
$set: set
|
||||||
}, { upsert: true });
|
}, { upsert: true });
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import $ from 'cafy';
|
||||||
|
import User, { pack } from '../../../../models/user';
|
||||||
|
import { publishMainStream } from '../../../../stream';
|
||||||
|
import define from '../../define';
|
||||||
|
import * as nodemailer from 'nodemailer';
|
||||||
|
import fetchMeta from '../../../../misc/fetch-meta';
|
||||||
|
import rndstr from 'rndstr';
|
||||||
|
import config from '../../../../config';
|
||||||
|
const ms = require('ms');
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
secure: true,
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 3
|
||||||
|
},
|
||||||
|
|
||||||
|
params: {
|
||||||
|
email: {
|
||||||
|
validator: $.str.optional.nullable
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||||
|
await User.update(user._id, {
|
||||||
|
$set: {
|
||||||
|
email: ps.email,
|
||||||
|
emailVerified: false,
|
||||||
|
emailVerifyCode: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serialize
|
||||||
|
const iObj = await pack(user._id, user, {
|
||||||
|
detail: true,
|
||||||
|
includeSecrets: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res(iObj);
|
||||||
|
|
||||||
|
// Publish meUpdated event
|
||||||
|
publishMainStream(user._id, 'meUpdated', iObj);
|
||||||
|
|
||||||
|
if (ps.email != null) {
|
||||||
|
const code = rndstr('a-z0-9', 16);
|
||||||
|
|
||||||
|
await User.update(user._id, {
|
||||||
|
$set: {
|
||||||
|
emailVerifyCode: code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const meta = await fetchMeta();
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: meta.smtpHost,
|
||||||
|
port: meta.smtpPort,
|
||||||
|
secure: meta.smtpSecure,
|
||||||
|
auth: {
|
||||||
|
user: meta.smtpUser,
|
||||||
|
pass: meta.smtpPass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const link = `${config.url}/vefify-email/${code}`;
|
||||||
|
|
||||||
|
transporter.sendMail({
|
||||||
|
from: meta.email,
|
||||||
|
to: ps.email,
|
||||||
|
subject: meta.name,
|
||||||
|
text: `To verify email, please click this link: ${link}`
|
||||||
|
}, (error, info) => {
|
||||||
|
if (error) {
|
||||||
|
return console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Message sent: %s', info.messageId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
|
@ -108,6 +108,13 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||||
response.discordClientId = instance.discordClientId;
|
response.discordClientId = instance.discordClientId;
|
||||||
response.discordClientSecret = instance.discordClientSecret;
|
response.discordClientSecret = instance.discordClientSecret;
|
||||||
response.summalyProxy = instance.summalyProxy;
|
response.summalyProxy = instance.summalyProxy;
|
||||||
|
response.enableEmail = instance.enableEmail;
|
||||||
|
response.email = instance.email;
|
||||||
|
response.smtpSecure = instance.smtpSecure;
|
||||||
|
response.smtpHost = instance.smtpHost;
|
||||||
|
response.smtpPort = instance.smtpPort;
|
||||||
|
response.smtpUser = instance.smtpUser;
|
||||||
|
response.smtpPass = instance.smtpPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
res(response);
|
res(response);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import config from '../config';
|
||||||
import networkChart from '../chart/network';
|
import networkChart from '../chart/network';
|
||||||
import apiServer from './api';
|
import apiServer from './api';
|
||||||
import { sum } from '../prelude/array';
|
import { sum } from '../prelude/array';
|
||||||
|
import User from '../models/user';
|
||||||
|
|
||||||
// Init app
|
// Init app
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
|
@ -59,6 +60,24 @@ const router = new Router();
|
||||||
router.use(activityPub.routes());
|
router.use(activityPub.routes());
|
||||||
router.use(webFinger.routes());
|
router.use(webFinger.routes());
|
||||||
|
|
||||||
|
router.get('/verify-email/:code', async ctx => {
|
||||||
|
const user = await User.findOne({ emailVerifyCode: ctx.params.code });
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
ctx.body = 'Verify succeeded!';
|
||||||
|
ctx.status = 200;
|
||||||
|
|
||||||
|
User.update({ _id: user._id }, {
|
||||||
|
$set: {
|
||||||
|
emailVerified: true,
|
||||||
|
emailVerifyCode: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ctx.status = 404;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Return 404 for other .well-known
|
// Return 404 for other .well-known
|
||||||
router.all('/.well-known/*', async ctx => {
|
router.all('/.well-known/*', async ctx => {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
|
|
Loading…
Reference in New Issue