226 lines
6.6 KiB
TypeScript
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 }
|
|
}
|
|
}
|