优化邮箱登录, 增加一个用于生成分享码的接口
This commit is contained in:
parent
c16817c480
commit
5001ca558f
@ -1,4 +1,5 @@
|
|||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
|
import { ShareCodeRecord, DEFAULT_SHARE_CODE, ShareCodeStatus, DEFAULT_EXPIRE_TIME } from 'modules/ShareCodeRecord'
|
||||||
import { BaseController, router } from 'zutils'
|
import { BaseController, router } from 'zutils'
|
||||||
|
|
||||||
class MainController extends BaseController {
|
class MainController extends BaseController {
|
||||||
@ -9,4 +10,25 @@ class MainController extends BaseController {
|
|||||||
await user.updateOne({ $inc: { accountVersion: 1 } })
|
await user.updateOne({ $inc: { accountVersion: 1 } })
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@router('post /wallet/code/generate')
|
||||||
|
async shareCode(req, res) {
|
||||||
|
logger.db('code_generate', req)
|
||||||
|
let user = req.user
|
||||||
|
let { type } = req.body
|
||||||
|
let record = await ShareCodeRecord.findOne({ account: user.id, type, status: ShareCodeStatus.PENDING })
|
||||||
|
if (!record) {
|
||||||
|
record = new ShareCodeRecord({
|
||||||
|
account: user.id,
|
||||||
|
openId: user.openId,
|
||||||
|
plat: user.plat + '',
|
||||||
|
code: DEFAULT_SHARE_CODE,
|
||||||
|
email: user.email,
|
||||||
|
type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
record.expiredAt = Date.now() + DEFAULT_EXPIRE_TIME
|
||||||
|
await record.save()
|
||||||
|
return { code: record.code }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ export function verifyPass(userpassword: string, passwordDb: string, salt: strin
|
|||||||
export interface AccountClass extends Base, TimeStamps {}
|
export interface AccountClass extends Base, TimeStamps {}
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ plat: 1, openId: 1 }, { unique: true })
|
@index({ plat: 1, openId: 1 }, { unique: true })
|
||||||
|
@index({ plat: 1, email: 1 }, { collation: { locale: 'en', strength: 2 } })
|
||||||
@modelOptions({ schemaOptions: { collection: 'account', timestamps: true }, options: { allowMixed: Severity.ALLOW } })
|
@modelOptions({ schemaOptions: { collection: 'account', timestamps: true }, options: { allowMixed: Severity.ALLOW } })
|
||||||
export class AccountClass extends BaseModule {
|
export class AccountClass extends BaseModule {
|
||||||
@prop({ enum: PlatEnum, default: PlatEnum.GOOGLE })
|
@prop({ enum: PlatEnum, default: PlatEnum.GOOGLE })
|
||||||
|
80
src/modules/ShareCodeRecord.ts
Normal file
80
src/modules/ShareCodeRecord.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { getModelForClass, index, modelOptions, pre, prop, ReturnModelType } from '@typegoose/typegoose'
|
||||||
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { BaseModule } from './Base'
|
||||||
|
|
||||||
|
import { customAlphabet } from 'nanoid'
|
||||||
|
|
||||||
|
const nanoid = customAlphabet('23456789abcdefghjkmnpqrstuvwxy', 8)
|
||||||
|
|
||||||
|
export const DEFAULT_SHARE_CODE = '00000000'
|
||||||
|
export const DEFAULT_EXPIRE_TIME = 5 * 60 * 1000
|
||||||
|
|
||||||
|
export enum ShareCodeType {
|
||||||
|
BIND_UAW = 1, // uaw 绑定
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ShareCodeStatus {
|
||||||
|
PENDING = 1,
|
||||||
|
SUCCESS = 2,
|
||||||
|
FAIL = 3,
|
||||||
|
EXPIRED = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享码记录
|
||||||
|
*/
|
||||||
|
@dbconn()
|
||||||
|
@index({ type: 1, code: 1 }, { unique: true, partialFilterExpression: { status: 1 } })
|
||||||
|
@index({ account: 1, type: 1, status: 1 }, { unique: true, partialFilterExpression: { status: 1 } })
|
||||||
|
@index({ expiredAt: 1, status: 1 }, { unique: false })
|
||||||
|
@modelOptions({
|
||||||
|
schemaOptions: { collection: 'share_code_record', timestamps: true },
|
||||||
|
})
|
||||||
|
@pre<ShareCodeRecordClass>('save', async function () {
|
||||||
|
if (this.code === DEFAULT_SHARE_CODE) {
|
||||||
|
let exists = false
|
||||||
|
while (!exists) {
|
||||||
|
const code = nanoid()
|
||||||
|
const record = await ShareCodeRecord.findByCode(code, this.type)
|
||||||
|
if (!record) {
|
||||||
|
exists = true
|
||||||
|
this.code = code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
class ShareCodeRecordClass extends BaseModule {
|
||||||
|
@prop({ required: true })
|
||||||
|
public account: string
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
public openId: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public email?: string
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
public plat: string
|
||||||
|
|
||||||
|
@prop({ required: true })
|
||||||
|
public code!: string
|
||||||
|
|
||||||
|
@prop({ default: Date.now() + DEFAULT_EXPIRE_TIME })
|
||||||
|
public expiredAt?: number
|
||||||
|
|
||||||
|
@prop({ required: true, default: ShareCodeType.BIND_UAW })
|
||||||
|
public type: ShareCodeType
|
||||||
|
|
||||||
|
@prop({ required: true, default: ShareCodeStatus.PENDING })
|
||||||
|
public status: ShareCodeStatus
|
||||||
|
|
||||||
|
public static async findByCode(
|
||||||
|
this: ReturnModelType<typeof ShareCodeRecordClass>,
|
||||||
|
code: string,
|
||||||
|
type: ShareCodeType,
|
||||||
|
) {
|
||||||
|
return this.findOne({ code, type, status: ShareCodeStatus.PENDING }).exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShareCodeRecord = getModelForClass(ShareCodeRecordClass, { existingConnection: ShareCodeRecordClass.db })
|
@ -2,6 +2,8 @@ import { ZError } from 'zutils'
|
|||||||
import { IPlat } from './IPlat'
|
import { IPlat } from './IPlat'
|
||||||
import { CodeRecord, CodeStatus, CodeType } from 'modules/CodeRecord'
|
import { CodeRecord, CodeStatus, CodeType } from 'modules/CodeRecord'
|
||||||
import { sha1 } from 'zutils/utils/security.util'
|
import { sha1 } from 'zutils/utils/security.util'
|
||||||
|
import { Account } from 'modules/Account'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
|
|
||||||
const isEmail = (email: string) => {
|
const isEmail = (email: string) => {
|
||||||
const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/
|
const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/
|
||||||
@ -34,7 +36,9 @@ export class PlatEmail implements IPlat {
|
|||||||
if (recordCode.status !== CodeStatus.PENDING) {
|
if (recordCode.status !== CodeStatus.PENDING) {
|
||||||
throw new ZError(13, 'code expired')
|
throw new ZError(13, 'code expired')
|
||||||
}
|
}
|
||||||
const openId = sha1(email)
|
email = email.toLowerCase()
|
||||||
|
let account = await Account.findOne({ plat: PlatEnum.EMAIL, email }).collation({ locale: 'en', strength: 2 })
|
||||||
|
const openId = account ? account.openId : sha1(email)
|
||||||
let data: any = { email, emailReal: email, emailVerified: true }
|
let data: any = { email, emailReal: email, emailVerified: true }
|
||||||
const { api_platform } = req.headers
|
const { api_platform } = req.headers
|
||||||
if (api_platform) {
|
if (api_platform) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { singleton } from 'zutils'
|
import { singleton } from 'zutils'
|
||||||
import { CodeRecord, CodeStatus } from 'modules/CodeRecord'
|
import { CodeRecord, CodeStatus } from 'modules/CodeRecord'
|
||||||
import * as schedule from 'node-schedule'
|
import * as schedule from 'node-schedule'
|
||||||
|
import { ShareCodeRecord } from 'modules/ShareCodeRecord'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时更新发送邮件验证码的过期状态
|
* 定时更新发送邮件验证码的过期状态
|
||||||
@ -10,6 +11,10 @@ export default class CodeTaskSchedule {
|
|||||||
async parseAllRecord() {
|
async parseAllRecord() {
|
||||||
let now = Date.now()
|
let now = Date.now()
|
||||||
await CodeRecord.updateMany({ expiredAt: { $lt: now }, status: CodeStatus.PENDING }, { status: CodeStatus.EXPIRED })
|
await CodeRecord.updateMany({ expiredAt: { $lt: now }, status: CodeStatus.PENDING }, { status: CodeStatus.EXPIRED })
|
||||||
|
await ShareCodeRecord.updateMany(
|
||||||
|
{ expiredAt: { $lt: now }, status: CodeStatus.PENDING },
|
||||||
|
{ status: CodeStatus.EXPIRED },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
scheduleAll() {
|
scheduleAll() {
|
||||||
const job = schedule.scheduleJob('*/1 * * * *', async () => {
|
const job = schedule.scheduleJob('*/1 * * * *', async () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user