wallet-svr/src/controllers/mail.controller.ts

226 lines
6.6 KiB
TypeScript

import { PlatEnum } from 'enums/PlatEnum'
import logger from 'logger/logger'
import { Account } from 'modules/Account'
import { CodeRecord, CodeStatus, CodeType, DEFAULT_CODE, DEFAULT_EXPIRE_TIME } from 'modules/CodeRecord'
import {
DEFAULT_LOGIN_MAIL_HTML,
DEFAULT_LOGIN_MAIL_SUBJECT,
DEFAULT_REGIST_HTML,
DEFAULT_REGIST_SUBJECT,
DEFAULT_RESET_HTML,
DEFAULT_RESET_SUBJECT,
DEFAULT_VERIFY_MAIL_HTML,
DEFAULT_VERIFY_MAIL_SUBJECT,
EmailSvr,
} from 'service/email.svr'
import { sha1, uuid } from 'zutils/utils/security.util'
import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
export const isEmail = (email: string) => {
const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/
return reg.test(email)
}
class MailController extends BaseController {
/**
* 通过邮件, 密码形式的登录
*/
@role(ROLE_ANON)
@router('post /wallet/login/email')
async loginWithEmail(req, res) {
logger.db('login', req)
let { email, password } = req.params
if (!email || !password) {
throw new ZError(10, 'params mismatch')
}
// check Account exists
let record = await Account.findByEmail(email)
if (!record) {
throw new ZError(11, 'account not exists')
}
if (record.locked) {
throw new ZError(12, 'account locked')
}
if (!record.verifyPassword(password)) {
throw new ZError(13, 'password error')
}
const token = await res.jwtSign({
id: record.id,
openid: record.openId,
version: record.accountVersion || 0,
plat: PlatEnum.EMAIL,
})
return { token: token }
}
/**
* 注册email账号
*/
@role(ROLE_ANON)
@router('post /email/regist')
async registMailAccount(req, res) {
logger.db('regist', req)
let { email, code, password } = req.params
if (!email || !code || !password) {
throw new ZError(10, 'params mismatch')
}
let account = await Account.findByEmail(email)
if (account) {
throw new ZError(11, 'account exists')
}
let record = await CodeRecord.findByEmail(email, CodeType.REGIST)
if (!record) {
throw new ZError(12, 'code not exists')
}
if (record.status !== CodeStatus.PENDING) {
throw new ZError(13, 'code expired')
}
if (record.code !== code) {
throw new ZError(14, 'code error')
}
account = new Account({ plat: PlatEnum.EMAIL, email, openId: uuid() })
account.updatePassword(password)
account.emailReal = email
account.emailVerified = true
const { api_platform } = req.headers
if (api_platform) {
account.platform = api_platform
}
await account.save()
record.status = CodeStatus.SUCCESS
await record.save()
const ztoken = await res.jwtSign({ id: account.id, openid: account.openId, plat: PlatEnum.EMAIL })
return { token: ztoken }
}
/**
* 通过email重置密码
*/
@role(ROLE_ANON)
@router('post /email/reset_password')
async resetMailPassword(req, res) {
logger.db('reset_pass', req)
let { email, code, password } = req.params
if (!email || !code || !password) {
throw new ZError(10, 'params mismatch')
}
let account = await Account.findByEmail(email)
if (!account) {
throw new ZError(11, 'account not exists')
}
let record = await CodeRecord.findByEmail(email, CodeType.RESET)
if (!record) {
throw new ZError(12, 'code not exists')
}
if (record.status !== CodeStatus.PENDING) {
throw new ZError(13, 'code expired')
}
if (record.code !== code) {
throw new ZError(14, 'code error')
}
account.updatePassword(password)
await account.save()
record.status = CodeStatus.SUCCESS
await record.save()
return {}
}
/**
* 发送验证码
*/
@role(ROLE_ANON)
@router('post /email/send_code')
async sendVerifyCode(req, res) {
logger.db('send_mail_code', req)
let { email, type } = req.params
if (!email || !type) {
throw new ZError(10, 'params mismatch')
}
if (!isEmail(email)) {
throw new ZError(12, 'email error')
}
type = parseInt(type)
if (type !== CodeType.REGIST && type !== CodeType.RESET && type !== CodeType.VERIFY && type !== CodeType.LOGIN) {
throw new ZError(13, 'type error')
}
const lockKey = sha1(`${email}_${type}`)
await new SyncLocker().checkLock(req, lockKey, 55000)
if (type === CodeType.REGIST) {
let account = await Account.findByEmail(email)
if (account) {
throw new ZError(11, 'account exists')
}
}
let record = await CodeRecord.findByEmail(email, type)
if (!record) {
record = new CodeRecord({ email, type, code: DEFAULT_CODE })
await record.save()
}
let html, subject
switch (type) {
case CodeType.REGIST:
html = DEFAULT_REGIST_HTML
subject = DEFAULT_REGIST_SUBJECT
break
case CodeType.RESET:
html = DEFAULT_RESET_HTML
subject = DEFAULT_RESET_SUBJECT
break
case CodeType.VERIFY:
html = DEFAULT_VERIFY_MAIL_HTML
subject = DEFAULT_VERIFY_MAIL_SUBJECT
case CodeType.LOGIN:
html = DEFAULT_LOGIN_MAIL_HTML
subject = DEFAULT_LOGIN_MAIL_SUBJECT
}
if (!html || !subject) {
throw new ZError(15, 'type error')
}
subject = record.code + ' ' + subject
html = html.replace('{{ocde}}', record.code)
html = html.replace('{{time}}', new Date().format('yyyy-MM-dd hh:mm:ss'))
let msgData = {
to: email,
html,
subject,
}
setImmediate(async () => {
try {
let { errcode, errmsg, data } = await new EmailSvr().sendMail(msgData)
if (errcode) {
logger.info(`error send mail:: email: ${email}, type: ${type}, errcode: ${errcode}, errmsg: ${errmsg}`)
record.status = CodeStatus.FAIL
} else {
logger.info(`success send mail:: email: ${email}, type: ${type}, messageId: ${data.messageId}`)
record.mailSend = true
record.emailId = data.messageId
record.expiredAt = Date.now() + DEFAULT_EXPIRE_TIME
}
await record.save()
} catch (err) {
logger.info(`error send mail:: email: ${email}, type: ${type}`)
logger.error(err)
record.status = CodeStatus.FAIL
await record.save()
throw new ZError(14, 'send mail error')
}
})
return {}
}
/**
* 检查email是否已经被注册
*/
@role(ROLE_ANON)
@router('post /email/check')
async checkMailExists(req, res) {
let { email } = req.params
if (!email) {
throw new ZError(10, 'params mismatch')
}
let account = await Account.findByEmail(email)
return { exists: !!account }
}
}