add task for okxlogin

This commit is contained in:
CounterFire2023 2024-01-04 15:15:50 +08:00
parent 2b5ae26454
commit cc85523359
8 changed files with 195 additions and 3 deletions

View File

@ -22,6 +22,16 @@
"autoclaim": false, "autoclaim": false,
"pretasks": ["TwitterConnect"], "pretasks": ["TwitterConnect"],
"params": {"time": 6, "failRate": 60} "params": {"time": 6, "failRate": 60}
}, {
"id": "OkxLogin",
"title": "okx wallet login",
"type": 1,
"desc": "",
"category": "",
"score": 100,
"autoclaim": false,
"pretasks": [],
"params": {}
}, { }, {
"id": "TwitterRetweet", "id": "TwitterRetweet",
"title": "ReTwitt", "title": "ReTwitt",

View File

@ -23,6 +23,7 @@
"@typegoose/typegoose": "^7.4.6", "@typegoose/typegoose": "^7.4.6",
"axios": "^0.21.1", "axios": "^0.21.1",
"bson": "^4.0.4", "bson": "^4.0.4",
"crypto-js": "^4.2.0",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",

View File

@ -4,8 +4,10 @@ import { role, router } from 'decorators/router'
import logger from 'logger/logger' import logger from 'logger/logger'
import { ActivityUser } from 'models/ActivityUser' import { ActivityUser } from 'models/ActivityUser'
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord' import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord'
import { LoginRecordQueue } from 'queue/loginrecord.queue'
import {SiweMessage} from 'siwe' import {SiweMessage} from 'siwe'
import { checkParamsNeeded } from 'utils/net.util' import { checkParamsNeeded } from 'utils/net.util'
import { aesDecrypt, base58ToHex } from 'utils/security.util'
const LOGIN_TIP = 'This signature is just to verify your identity' const LOGIN_TIP = 'This signature is just to verify your identity'
@ -18,7 +20,7 @@ class SignController extends BaseController {
await record.save() await record.save()
return { nonce: record.id, tips: LOGIN_TIP } return { nonce: record.id, tips: LOGIN_TIP }
} }
//TODO:: 增加okx的奖励
@role(ROLE_ANON) @role(ROLE_ANON)
@router('post /api/wallet/login') @router('post /api/wallet/login')
async walletVerify(req, res) { async walletVerify(req, res) {
@ -27,7 +29,20 @@ class SignController extends BaseController {
if (!message.nonce) { if (!message.nonce) {
throw new ZError(11, 'Invalid 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) { if (!record || record.status !== 0) {
throw new ZError(12, 'nonce invalid') throw new ZError(12, 'nonce invalid')
} }
@ -41,7 +56,7 @@ class SignController extends BaseController {
await record.save() await record.save()
const msgSign = new SiweMessage(message); const msgSign = new SiweMessage(message);
try { try {
await msgSign.verify({ signature, nonce: record.id }); await msgSign.verify({ signature, nonce: message.nonce });
} catch (e) { } catch (e) {
throw new ZError(15, 'signature invalid') throw new ZError(15, 'signature invalid')
} }
@ -54,6 +69,7 @@ class SignController extends BaseController {
} }
accountData.lastLogin = Date.now() accountData.lastLogin = Date.now()
await accountData.save() 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 }) const token = await res.jwtSign({ id: accountData.id, address: accountData.address, activity: accountData.activity })
return { token } return { token }

26
src/models/LoginRecord.ts Normal file
View 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'] })

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

View File

@ -1,4 +1,5 @@
import crypto from 'crypto' import crypto from 'crypto'
import CryptoJS from 'crypto-js'
export function hmac(input, key, out) { export function hmac(input, key, out) {
return out return out
@ -73,3 +74,66 @@ export function checkSign({
let sign1 = hmacSha256(signStr, secretKey) let sign1 = hmacSha256(signStr, secretKey)
return sign1 === sign 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);
}

View File

@ -1551,6 +1551,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" 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: d@1, d@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz" resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz"