diff --git a/src/controllers/verify.controller.ts b/src/controllers/verify.controller.ts new file mode 100644 index 0000000..3973e9c --- /dev/null +++ b/src/controllers/verify.controller.ts @@ -0,0 +1,53 @@ +import BaseController from "common/base.controller" +import {ZError} from "common/ZError" +import {role, router} from "decorators/router" +import {Account} from "modules/Account" +import {DEFAULT_VERIFY_HTML, EmailSvr} from "service/email.svr" + +const TOKEN_PREFIX = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' +class VerifyController extends BaseController { + + @router('post /email/verify') + async sendVerifyEmail(req, res) { + let user = req.user + let { email } = req.params + if (!user.email && !email) { + throw new ZError(10, 'params mismatch') + } + email = email || user.email + let token = await res.jwtSign({ id: user.id }) + token = token.replace(TOKEN_PREFIX, '') + let url = process.env.EMAIL_VERIFY_URL + '/email/verify?k='+token + let html = DEFAULT_VERIFY_HTML.replace('{{href}}', url) + user.emailReal = email + await user.save() + let msgData = { + to: email, + html + } + let result = await new EmailSvr().sendMail(msgData) + return result + } + + @role('anon') + @router('get /email/verify') + async verifyEmail(req, res) { + let { k } = req.params + if (!k) { + throw new ZError(10, 'params mismatch') + } + const token = TOKEN_PREFIX + k; + const reqData = await req.jwtVerify({ extractToken: () => token}) + if (!reqData || !reqData.id) { + throw new ZError(12, 'token error') + } + let user = await Account.findById(reqData.id) + if (!user) { + throw new ZError(13, 'user not found') + } + user.verified = true + user.emailVerifyTime = Date.now() + await user.save() + return {} + } +} diff --git a/src/modules/Account.ts b/src/modules/Account.ts index a2d549b..6acc999 100644 --- a/src/modules/Account.ts +++ b/src/modules/Account.ts @@ -24,8 +24,15 @@ class AccountClass extends BaseModule { public nickname?: string @prop() public avatar?: string + /** + * 第三方登录信息中返回的email, 会根据返回信息更新 + * 实际使用中使用下面的 emailReal + */ @prop() public email?: string + /** + * 第三方登录信息中返回的email认证信息 + */ @prop({ required: true, default: false }) public emailVerified: boolean @prop({ default: 0 }) @@ -48,8 +55,21 @@ class AccountClass extends BaseModule { public refreshToken?: string @prop() public refreshTokenExpire?: number - @prop({type: mongoose.Schema.Types.Mixed}) - public scope?: any + @prop({ type: mongoose.Schema.Types.Mixed }) + public scope?: any + /** + * 是否通过了邮件认证(我们发起的) + */ + @prop({ required: true, default: false }) + public verified: boolean + /** + * 认证后的email + */ + @prop() + public emailReal?: string + + @prop() + public emailVerifyTime?: number } export const Account = getModelForClass(AccountClass, { existingConnection: AccountClass.db }) diff --git a/src/net/NetClient.ts b/src/net/NetClient.ts index b2a6839..2c90807 100644 --- a/src/net/NetClient.ts +++ b/src/net/NetClient.ts @@ -25,8 +25,10 @@ export class NetClient { request(data: AxiosRequestConfig): Promise { let defaultCfg: AxiosRequestConfig = { method: 'get', + headers: {'Content-Type': 'application/json'} } Object.assign(defaultCfg, data) + console.log(defaultCfg) return axios(defaultCfg).then(res => res.data) } } diff --git a/src/service/email.svr.ts b/src/service/email.svr.ts new file mode 100644 index 0000000..b8899f6 --- /dev/null +++ b/src/service/email.svr.ts @@ -0,0 +1,37 @@ +import {singleton} from "decorators/singleton"; +import { NetClient} from "net/NetClient"; + +export const DEFAULT_VERIFY_HTML = +` +

Email Verification

+

CEBG needs to confirm your email address is still valid. Please click the link below to confirm you received this mail.

+

Verify Email

+

If you're worried about this email being legitimate, you can visit CEBG directly to confirm your email needs verifying. After doing so, please don't forget to click the link above.

+` +export interface IMailData { + from?: string + to: string + subject?: string + text?: string + html?: string +} + +const DEFAULT_MSG_DATA: IMailData = { + from: '自己人 ', + to: '', + subject: 'Please verify your email address' +} +const MAIL_SVR = 'http://127.0.0.1:3087' + +@singleton +export class EmailSvr { + public sendMail(msg: IMailData) { + Object(DEFAULT_MSG_DATA).zssign(msg) + let reqData = { + url: MAIL_SVR+'/mail/send', + data: JSON.stringify({message: msg}) + } + return new NetClient().httpPost(reqData) + } +} +