task-svr/src/controllers/mail.controller.ts
2024-05-15 20:12:32 +08:00

176 lines
5.6 KiB
TypeScript

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 {}
}
}