import { ZError, SyncLocker, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils' import { BOOST_CFG } from 'configs/boost' import { ActivityUser } from 'models/ActivityUser' import { DEFAULT_EXPIRED, NonceRecord } from 'models/NonceRecord' import { ScoreRecord } from 'models/ScoreRecord' import { LoginRecordQueue } from 'queue/loginrecord.queue' import { queryCheckInList } from 'services/chain.svr' import { rankKey } from 'services/rank.svr' import { SiweMessage } from 'siwe' import { nextday } from 'zutils/utils/date.util' import { checkParamsNeeded } from 'zutils/utils/net.util' import { aesDecrypt, base58ToHex } from 'zutils/utils/security.util' const LOGIN_TIP = 'This signature is just to verify your identity' const ROUND = 1000000 const generateBoost = (rewards: { probability: number }[]) => { let total = 0 let random = Math.floor(Math.random() * ROUND) let reward = null for (let r of rewards) { total += r.probability if (random < total) { reward = r break } } return reward.value } class SignController extends BaseController { @role(ROLE_ANON) @router('get /api/wallet/nonce') async walletNonce(req, res) { const { address } = req.params let record = new NonceRecord({ expired: Date.now() + DEFAULT_EXPIRED, address }) await record.save() return { nonce: record.id, tips: LOGIN_TIP } } @role(ROLE_ANON) @router('post /api/wallet/login') async walletVerify(req, res) { const { signature, message, activity } = req.params checkParamsNeeded(signature, message) if (!message.nonce) { throw new ZError(11, 'Invalid nonce') } let nonce = message.nonce let source = 'unknow' if (nonce.length > 24) { nonce = base58ToHex(nonce) let nonceStr = aesDecrypt(nonce, activity) if (nonceStr.indexOf('|') >= 0) { const split = nonceStr.split('|') nonce = split[0] source = split[1] } else { nonce = nonceStr } } let record = await NonceRecord.findById(nonce) if (!record || record.status !== 0) { throw new ZError(12, 'nonce invalid') } if (record.expired < Date.now()) { throw new ZError(13, 'nonce expired') } if (record.address.toLowerCase() !== message.address.toLowerCase()) { throw new ZError(14, 'address not match') } record.status = 1 await record.save() const msgSign = new SiweMessage(message) try { await msgSign.verify({ signature, nonce: message.nonce }) } catch (e) { throw new ZError(15, 'signature invalid') } let accountData = await ActivityUser.insertOrUpdate({ address: message.address, activity }, {}) if (!accountData) { throw new ZError(11, 'user not found') } if (accountData.locked) { throw new ZError(12, 'user locked') } accountData.lastLogin = new Date() await accountData.save() new LoginRecordQueue().addLog(req, accountData.id, activity, source) const token = await res.jwtSign({ id: accountData.id, address: accountData.address, activity: accountData.activity, }) return { token } } @router('get /api/user/state') async userInfo(req) { const user = req.user const todayKey = rankKey(user.activity, new Date()) const todayScore = await new ZRedisClient().zscore(todayKey, user.id) const totalKey = rankKey(user.activity) const totalScore = await new ZRedisClient().zscore(totalKey, user.id) const totalRank = await new ZRedisClient().zrevrank(totalKey, user.id) let invite = '' if (user.inviteUser) { const inviteUser = await ActivityUser.findById(user.inviteUser) if (inviteUser) { invite = inviteUser.address } } const records = await ScoreRecord.find({ user: user.id, activity: user.activity, scoreType: 'invite' }) let score = 0 for (let record of records) { score += record.score } let result = { address: user.address, boost: user.boost || 1, boostExpire: user.boostExpire, twitterId: user.twitterId, twitterName: user.twitterName, discordId: user.discordId, discordName: user.discordName, scoreToday: todayScore ? parseInt(todayScore + '') : 0, scoreTotal: totalScore ? parseInt(totalScore + '') : 0, rankTotal: totalRank ? totalRank : '-', invite, inviteCount: records.length, inviteScore: score, code: user.inviteCode, } return result } @router('get /api/user/state/boost') async boost(req) { new SyncLocker().checkLock(req) const user = req.user if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) { throw new ZError(11, 'already boosted') } user.boost = generateBoost(BOOST_CFG) user.boostExpire = nextday() await user.save() return { boost: user.boost, boostExpire: user.boostExpire } } @router('get /api/user/checkin/list/:tag') async checkInList(req) { const user = req.user const { tag } = req.params let days: any = 1 if (tag === '1month') { days = '1month' } else if (tag === 'last') { days = '1' } const res = await queryCheckInList(user.address, days, 0) return res.data } /** * regist user by token from wallet-svr * TODO:: * @param req * @param res * @returns */ @role(ROLE_ANON) @router('post /api/wallet/verify_token') async verifyToken(req, res) { const { wallet_token } = req.params return {} } }