diff --git a/src/controllers/account.controller.ts b/src/controllers/account.controller.ts index 3e82ff4..a68d525 100644 --- a/src/controllers/account.controller.ts +++ b/src/controllers/account.controller.ts @@ -1,4 +1,4 @@ -import { permission, role, router } from 'decorators/router' +import { role, router } from 'decorators/router' import BaseController, { ROLE_ANON } from 'common/base.controller' import { ZError } from 'common/ZError' import { NonceRecord } from 'models/NonceRecord' @@ -9,7 +9,7 @@ import { User } from 'models/User' const LOGIN_TIP = 'This signature is just to verify your identity' export default class AccountController extends BaseController { @role(ROLE_ANON) - @router('get /api/wallet/wallet_nonce') + @router('get /api/user/login') async walletNonce(req, res) { let record = new NonceRecord() await record.save() @@ -17,49 +17,65 @@ export default class AccountController extends BaseController { } // wallet login @role(ROLE_ANON) - @router('post /api/wallet/wallet_login') + @router('post /api/user/login') async loginWallet(req, res) { - const { nonce, signature, account } = req.params - logger.info(`wallet login::account:${account} nonce:${nonce} signature:${signature}`) - if (!nonce || !signature || !account) { + const { nonce, signature, address } = req.params + logger.db('login', req) + logger.info(`wallet login::address:${address} nonce:${nonce} signature:${signature}`) + if (!nonce || !signature || !address) { throw new ZError(10, 'params mismatch') } let record = await NonceRecord.findById(nonce) if (!record || record.status !== 0) { throw new ZError(11, 'nonce invalid') } + if (record.expired < Date.now()) { + throw new ZError(12, 'nonce expired') + } record.status = 1 await record.save() const signObj = buildLoginSignMsg(nonce, LOGIN_TIP) const recover = recoverTypedSignatureV4(signObj, signature) - if (recover !== account) { + if (recover !== address) { throw new ZError(20, 'address mismatch') } - let accountData = await User.findByAddress(account) + let accountData = await User.insertOrUpdate({ address }, {}) if (!accountData) { throw new ZError(11, 'user not found') } if (accountData.locked) { throw new ZError(12, 'user locked') } + accountData.lastLogin = Date.now() + await accountData.save() const token = await res.jwtSign({ id: accountData.id }) + setImmediate(() => { + NonceRecord.removeExpired() + }) return { token } } - @router('post /api/wallet/logout') - async logout(req, res) { - return {} - } - - @router('get /api/user/:uid/info') + @router('get /api/user/status') async info(req) { - let uid = req.params.uid - const account = await User.findById(uid) - return { account: account, user: req.user } + let user = req.user + return {} } + @router('post /api/user/claim') async claim(req, res) { - return {} + let user = req.user + logger.db('claim', req) + const {} = req.params + const taskId = '' + return { taskId } + } + + @router('get /api/user/claim/:taskId') + async claimStatus(req, res) { + const { taskId } = req.params + const txHashList = [] + let status = 0 + return { status, txHashList } } } diff --git a/src/logger/logger.ts b/src/logger/logger.ts index 8bbf745..4b95b00 100644 --- a/src/logger/logger.ts +++ b/src/logger/logger.ts @@ -1,3 +1,9 @@ +import { LoggerQueue } from 'queue/logger.queue' + const level = process.env.NODE_ENV === 'production' ? 'info' : 'log' const logger = require('tracer').colorConsole({ dateformat: 'yyyy-mm-dd HH:MM:ss.L', level }) +logger.db = function (name: string, req: any, logObj?: any) { + logObj = logObj || {} + new LoggerQueue().addLog(name, req, logObj) +} export default logger diff --git a/src/models/NonceRecord.ts b/src/models/NonceRecord.ts index efdc739..daf0e78 100644 --- a/src/models/NonceRecord.ts +++ b/src/models/NonceRecord.ts @@ -2,11 +2,19 @@ import { getModelForClass, index, modelOptions, pre, prop } from '@typegoose/typ import { dbconn } from 'decorators/dbconn' import { BaseModule } from './Base' +const DEFAULT_EXPIRED = 1000 * 60 * 5 @dbconn() @modelOptions({ schemaOptions: { collection: 'nonce_record', timestamps: true } }) class NonceRecordClass extends BaseModule { @prop({ required: true, default: 0 }) public status: number + + @prop({ default: Date.now() + DEFAULT_EXPIRED }) + public expired: number + + public static async removeExpired() { + await NonceRecord.deleteMany({ expired: { $lt: Date.now() } }) + } } export const NonceRecord = getModelForClass(NonceRecordClass, { existingConnection: NonceRecordClass['db'] }) diff --git a/src/models/UserLog.ts b/src/models/UserLog.ts new file mode 100644 index 0000000..9b4a5c1 --- /dev/null +++ b/src/models/UserLog.ts @@ -0,0 +1,31 @@ +import { dbconn } from 'decorators/dbconn' +import { getModelForClass, index, modelOptions, mongoose, prop } from '@typegoose/typegoose' +import { Severity } from '@typegoose/typegoose/lib/internal/constants' +import { BaseModule } from './Base' + +/** + * 管理员操作记录 + */ +@dbconn() +@index({ user: 1 }, { unique: false }) +@index({ name: 1 }, { unique: false }) +@modelOptions({ schemaOptions: { collection: 'user_log', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) +class UserLogClass extends BaseModule { + @prop() + public user: string + @prop() + public name: string + @prop() + public method: string + @prop() + public path: string + @prop() + public referer: string + @prop() + public user_agent: string + @prop() + public ip: string + @prop({ type: mongoose.Schema.Types.Mixed }) + public params: any +} +export const UserLog = getModelForClass(UserLogClass, { existingConnection: UserLogClass['db'] }) diff --git a/src/queue/logger.queue.ts b/src/queue/logger.queue.ts new file mode 100644 index 0000000..1ffb033 --- /dev/null +++ b/src/queue/logger.queue.ts @@ -0,0 +1,39 @@ +import { AsyncQueue, createAsyncQueue } from 'common/AsyncQueue' +import { singleton } from 'decorators/singleton' +import logger from 'logger/logger' +import { UserLog } from 'models/UserLog' + +@singleton +export class LoggerQueue { + private queue: AsyncQueue + + constructor() { + this.queue = createAsyncQueue() + } + + public addLog(name, req, logObj) { + this.queue.push(async () => { + const user = req.user + const ip = req.headers['x-forwarded-for'] || req.ip + const path = req.url + const params = req.method === 'GET' ? req.query : req.body + const dataObj = JSON.stringify(logObj) === '{}' ? params : logObj + try { + const history = new UserLog({ + user: user ? user.id : '', + path: path, + method: req.method, + params: dataObj, + referer: req.headers['referer'], + user_agent: req.headers['user-agent'], + ip, + name, + }) + await history.save() + } catch (err) { + logger.error('error add user log: ') + logger.error(err) + } + }) + } +}