add task for okxlogin
This commit is contained in:
parent
2b5ae26454
commit
cc85523359
@ -22,6 +22,16 @@
|
||||
"autoclaim": false,
|
||||
"pretasks": ["TwitterConnect"],
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "OkxLogin",
|
||||
"title": "okx wallet login",
|
||||
"type": 1,
|
||||
"desc": "",
|
||||
"category": "",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": [],
|
||||
"params": {}
|
||||
}, {
|
||||
"id": "TwitterRetweet",
|
||||
"title": "ReTwitt",
|
||||
|
@ -23,6 +23,7 @@
|
||||
"@typegoose/typegoose": "^7.4.6",
|
||||
"axios": "^0.21.1",
|
||||
"bson": "^4.0.4",
|
||||
"crypto-js": "^4.2.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
|
@ -4,8 +4,10 @@ import { role, router } from 'decorators/router'
|
||||
import logger from 'logger/logger'
|
||||
import { ActivityUser } from 'models/ActivityUser'
|
||||
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord'
|
||||
import { LoginRecordQueue } from 'queue/loginrecord.queue'
|
||||
import {SiweMessage} from 'siwe'
|
||||
import { checkParamsNeeded } from 'utils/net.util'
|
||||
import { aesDecrypt, base58ToHex } from 'utils/security.util'
|
||||
|
||||
const LOGIN_TIP = 'This signature is just to verify your identity'
|
||||
|
||||
@ -18,7 +20,7 @@ class SignController extends BaseController {
|
||||
await record.save()
|
||||
return { nonce: record.id, tips: LOGIN_TIP }
|
||||
}
|
||||
|
||||
//TODO:: 增加okx的奖励
|
||||
@role(ROLE_ANON)
|
||||
@router('post /api/wallet/login')
|
||||
async walletVerify(req, res) {
|
||||
@ -27,7 +29,20 @@ class SignController extends BaseController {
|
||||
if (!message.nonce) {
|
||||
throw new ZError(11, 'Invalid nonce');
|
||||
}
|
||||
let record = await NonceRecord.findById(message.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')
|
||||
}
|
||||
@ -41,7 +56,7 @@ class SignController extends BaseController {
|
||||
await record.save()
|
||||
const msgSign = new SiweMessage(message);
|
||||
try {
|
||||
await msgSign.verify({ signature, nonce: record.id });
|
||||
await msgSign.verify({ signature, nonce: message.nonce });
|
||||
} catch (e) {
|
||||
throw new ZError(15, 'signature invalid')
|
||||
}
|
||||
@ -54,6 +69,7 @@ class SignController extends BaseController {
|
||||
}
|
||||
accountData.lastLogin = Date.now()
|
||||
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 }
|
||||
|
26
src/models/LoginRecord.ts
Normal file
26
src/models/LoginRecord.ts
Normal file
@ -0,0 +1,26 @@
|
||||
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, activity: 1, wallet: 1 }, { unique: false })
|
||||
@modelOptions({ schemaOptions: { collection: 'user_login_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } })
|
||||
class LoginRecordClass extends BaseModule {
|
||||
@prop()
|
||||
public user: string
|
||||
@prop()
|
||||
public wallet: string
|
||||
@prop()
|
||||
public activity: string
|
||||
@prop()
|
||||
public referer: string
|
||||
@prop()
|
||||
public user_agent: string
|
||||
@prop()
|
||||
public ip: string
|
||||
}
|
||||
export const LoginRecord = getModelForClass(LoginRecordClass, { existingConnection: LoginRecordClass['db'] })
|
33
src/queue/loginrecord.queue.ts
Normal file
33
src/queue/loginrecord.queue.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { AsyncQueue, createAsyncQueue } from 'common/AsyncQueue'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import logger from 'logger/logger'
|
||||
import { LoginRecord } from 'models/LoginRecord'
|
||||
|
||||
@singleton
|
||||
export class LoginRecordQueue {
|
||||
private queue: AsyncQueue
|
||||
|
||||
constructor() {
|
||||
this.queue = createAsyncQueue()
|
||||
}
|
||||
|
||||
public addLog(req, user, activity, wallet) {
|
||||
this.queue.push(async () => {
|
||||
const ip = req.headers['x-forwarded-for'] || req.ip
|
||||
try {
|
||||
const history = new LoginRecord({
|
||||
user,
|
||||
activity,
|
||||
wallet,
|
||||
referer: req.headers['referer'],
|
||||
user_agent: req.headers['user-agent'],
|
||||
ip,
|
||||
})
|
||||
await history.save()
|
||||
} catch (err) {
|
||||
logger.error('error add user login record: ')
|
||||
logger.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
37
src/tasks/OkxLogin.ts
Normal file
37
src/tasks/OkxLogin.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { ITask } from "./base/ITask";
|
||||
import { ZError } from "common/ZError";
|
||||
import { TaskStatusEnum } from "models/ActivityUser";
|
||||
import { TaskCfg } from "models/ActivityInfo";
|
||||
import { UserLog } from "models/UserLog";
|
||||
import { LoginRecord } from "models/LoginRecord";
|
||||
|
||||
export default class OkxLogin extends ITask {
|
||||
static desc = 'okx wallet login'
|
||||
static show: boolean = true
|
||||
|
||||
async execute(data: any) {
|
||||
const { task } = data
|
||||
const { activity } = this.params.user
|
||||
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name)
|
||||
let wallet = 'okx';
|
||||
let record = LoginRecord.findOne({ user: this.params.user.id, activity, wallet})
|
||||
if (!record ) {
|
||||
throw new ZError(11, 'task not finished')
|
||||
}
|
||||
task.status = TaskStatusEnum.SUCCESS
|
||||
task.timeFinish = Date.now()
|
||||
try {
|
||||
await this.params.user.save()
|
||||
} catch(err) {
|
||||
throw new ZError(100, 'discord already binded')
|
||||
}
|
||||
if (cfg.autoclaim) {
|
||||
try {
|
||||
await this.claimReward(task);
|
||||
} catch(err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import crypto from 'crypto'
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
export function hmac(input, key, out) {
|
||||
return out
|
||||
@ -73,3 +74,66 @@ export function checkSign({
|
||||
let sign1 = hmacSha256(signStr, secretKey)
|
||||
return sign1 === sign
|
||||
}
|
||||
|
||||
// export function aesDecrypt(encrypted: string, key: string) {
|
||||
// let bytes = aes.decrypt(encrypted, key)
|
||||
// var originalText = bytes.toString(enc.Utf8);
|
||||
// return originalText
|
||||
// }
|
||||
|
||||
export const aesEncrypt = (plaintText, key) => {
|
||||
key = CryptoJS.SHA1(key).toString().substring(0,16)
|
||||
key = CryptoJS.enc.Base64.parse(key)
|
||||
let encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
});
|
||||
|
||||
return encryptedData.toString(CryptoJS.format.Hex);
|
||||
}
|
||||
|
||||
export const aesDecrypt = (encryptedDataHexStr, key) => {
|
||||
key = CryptoJS.SHA1(key).toString().substring(0,16)
|
||||
key = CryptoJS.enc.Base64.parse(key)
|
||||
let encryptedHex = CryptoJS.enc.Hex.parse(encryptedDataHexStr);
|
||||
let encryptedBase64 = CryptoJS.enc.Base64.stringify(encryptedHex);
|
||||
|
||||
var decryptedData = CryptoJS.AES.decrypt(encryptedBase64, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
|
||||
return decryptedData.toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
|
||||
|
||||
const base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
|
||||
export const hexToBase58 = (hexString: string) => {
|
||||
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16));
|
||||
let base58String = '';
|
||||
|
||||
let num = BigInt('0x' + hexString);
|
||||
while (num > BigInt(0)) {
|
||||
const remainder = num % BigInt(58);
|
||||
num = num / BigInt(58);
|
||||
base58String = base58Alphabet[Number(remainder)] + base58String;
|
||||
}
|
||||
|
||||
return base58String;
|
||||
}
|
||||
|
||||
export const base58ToHex = (base58String: string) => {
|
||||
const base58Length = base58String.length;
|
||||
let num = BigInt(0);
|
||||
let leadingZeros = 0;
|
||||
for (let i = 0; i < base58Length; i++) {
|
||||
const charIndex = base58Alphabet.indexOf(base58String[i]);
|
||||
if (charIndex === -1) {
|
||||
throw new Error('Invalid Base58 string');
|
||||
}
|
||||
|
||||
num = num * BigInt(58) + BigInt(charIndex);
|
||||
}
|
||||
return num.toString(16);
|
||||
}
|
@ -1551,6 +1551,11 @@ cross-spawn@^7.0.2:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto-js@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
|
||||
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
|
||||
|
||||
d@1, d@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz"
|
||||
|
Loading…
x
Reference in New Issue
Block a user