import logger from 'logger/logger' import { ActivityUser } from 'models/ActivityUser' import { CodeRecord, CodeStatus, CodeType, DEFAULT_CODE, DEFAULT_EXPIRE_TIME, isEmail, isValiedCode, } from 'models/CodeRecord' import { DEFAULT_LOGIN_MAIL_HTML, DEFAULT_LOGIN_MAIL_SUBJECT, EmailSvr } from 'services/email.svr' import { BaseController, router, ZError } from 'zutils' import { sha1 } from 'zutils/utils/security.util' import { SyncLocker } from 'common/SyncLocker' import { ShareCodeRecord, ShareCodeStatus, ShareCodeType } from 'models/wallet/ShareCodeRecord' export const isValiedShareCode = (code: string) => { return /^[23456789abcdefghjkmnpqrstuvwxy]{8}$/.test(code) } class MailController extends BaseController { /** * 通过邮件验证码形式的登录 */ // @router('post /api/user/verify_email') async loginWithEmail(req, res) { await new SyncLocker().checkLock(req) logger.db('verify_email', req) let user = req.user const { email, code } = req.params if (!email || !code) { throw new ZError(10, 'params mismatch') } if (!isEmail(email)) { throw new ZError(11, 'Invalid email') } if (!isValiedCode(code)) { throw new ZError(11, 'code error') } if (user.gameAccountBinded()) { throw new ZError(12, 'already bind game account') } let openId = sha1(email) let userCheck = await ActivityUser.findOne({ emailId: openId }) if (userCheck && userCheck.id !== user.id) { throw new ZError(13, 'Email already binded to another account') } let recordCode = await CodeRecord.findByEmail(user.id, email, CodeType.LOGIN) if (!recordCode) { throw new ZError(14, 'code expired') } if (recordCode.code !== code) { throw new ZError(16, 'code error') } user.emailId = openId user.email = email recordCode.status = CodeStatus.SUCCESS await recordCode.save() await user.save() return {} } @router('post /api/user/verify_client') async loginWithGameClient(req, res) { await new SyncLocker().checkLock(req) logger.db('verify_client', req) let user = req.user const { code } = req.params if (!code) { throw new ZError(10, 'params mismatch') } if (!isValiedShareCode(code)) { throw new ZError(11, 'code error') } if (user.gameAccountBinded()) { throw new ZError(12, 'already bind game account') } let recordCode = await ShareCodeRecord.findByCode(code, ShareCodeType.BIND_UAW) if (!recordCode) { throw new ZError(14, 'code expired') } const openId = recordCode.openId let userCheck = await ActivityUser.findOne({ clientId: openId, clientPlat: recordCode.plat }) let userCheck2 = await ActivityUser.findOne({ googleId: openId }) let userCheck3 = await ActivityUser.findOne({ emailId: openId }) if ( (userCheck && userCheck.id !== user.id) || (userCheck2 && userCheck2.id !== user.id) || (userCheck3 && userCheck3.id !== user.id) ) { throw new ZError(13, 'Email already binded to another account') } user.clientId = openId user.clientMail = recordCode.email user.clientPlat = recordCode.plat recordCode.status = ShareCodeStatus.SUCCESS await ShareCodeRecord.updateOne( { code, type: ShareCodeType.BIND_UAW, status: ShareCodeStatus.PENDING }, { status: ShareCodeStatus.SUCCESS }, ) await user.save() return {} } /** * 发送验证码 */ // @router('post /api/email/send_code') async sendVerifyCode(req, res) { await new SyncLocker().checkLock(req) logger.db('send_mail_code', req) let user = req.user let { email, type } = req.params type = type || CodeType.LOGIN if (!email) { throw new ZError(10, 'params mismatch') } if (!isEmail(email)) { throw new ZError(11, 'Invalid email') } if (user.gameAccountBinded()) { throw new ZError(12, 'already bind game account') } let openId = sha1(email) let userCheck = await ActivityUser.findOne({ emailId: openId }) if (userCheck && userCheck.id !== user.id) { throw new ZError(13, 'Email already binded to another account') } type = parseInt(type) let record = await CodeRecord.findByEmail(user.id, email, type) if (!record || record.user !== user.id) { record = new CodeRecord({ email, type, code: DEFAULT_CODE, user: user.id }) await record.save() } let html, subject switch (type) { 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}, errmsg: ${err.message || err}`) record.status = CodeStatus.FAIL await record.save() } }) return {} } }