/* eslint-disable no-cond-assign */ import type { KeyPairHex } from 'sm-crypto' import { sm2 } from 'sm-crypto' import type { OnOpenHandler, WsHandler } from './wssocket' import { WsSocket } from './wssocket' interface ResponseData { action: string responseID?: string status: true | false | string // 'Success' | 'Exception' | 'Error' | 'failed: no session' result?: unknown data: string [K: string]: unknown } // interface ListResponseData { // action: string // data: string // status: true | false // } interface PromiseCallbackPair { resolve: (value: ResponseData | PromiseLike) => void reject: (reason?: unknown) => void } export class WsClient { private readonly sm2Key: KeyPairHex private readonly wssocket: WsSocket private readonly promiseCallbackPairs: Record = {} private readonly sessionPromise: Promise private sessionResolve!: (value: string | PromiseLike) => void private readonly loginPromise: Promise private loginResolve!: (value: boolean | PromiseLike) => void constructor( url: string, onopen: OnOpenHandler, handler: WsHandler, sm2Key: KeyPairHex = sm2.generateKeyPairHex(), ) { this.sm2Key = sm2Key this.sessionPromise = new Promise((resolve, _reject) => { this.sessionResolve = resolve }) this.loginPromise = new Promise((resolve, _reject) => { this.loginResolve = resolve }) // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this this.wssocket = new WsSocket(url, onopen, function ( this: WebSocket, event, _, ) { const data = JSON.parse(event.data) as ResponseData switch (data.action) { case 'onSessionID': that.sessionResolve(data.session as string) break case 'onLogin': { const failed = typeof data.status === 'string' && data.status.toLowerCase().includes('failed') that.loginResolve(!failed) } break } const reqId = data.responseID let pc: PromiseCallbackPair | undefined if (reqId && (pc = that.promiseCallbackPairs[reqId])) { if (data) { pc.resolve(data) } else { pc.reject(data) } } handler(event, this) }) } status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'] { return this.wssocket.status() } sessionReceived(): Promise { return this.sessionPromise } async login(): Promise { const session = await this.sessionPromise const request = { action: 'login', pubKey: this.sm2Key.publicKey, signature: sm2.doSignature(session, this.sm2Key.privateKey, { hash: true, der: true, }), } this.wssocket.send(JSON.stringify(request)) return this.loginPromise } loggedIn(): Promise { return this.loginPromise } matchCID(contractID: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'matchCID', contractID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMetabyCID(contractID: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMetabyCID', contractID, requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMetabyReadme( keyword: string, page?: string, pageSize?: string, ): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMetabyReadme', page, pageSize, keyword, requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMetabyPubkey(pubkey: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMetabyPubkey', pubkey, requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } segmentWord(words: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { words, action: 'segmentWord', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMetabyOwner( owner: string, page?: string, pageSize?: string, ): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMetabyOwner', owner, page, pageSize, requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getDependentContract(contractName: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getDependentContract', requestID, contractName, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } queryContractLogByDate(start: number): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'queryContractLogByDate', requestID, start, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } queryDataByHash(hash: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'queryDataByHash', requestID, hash, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } executeContract( contractID: string, method: string, arg: unknown, ): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const argStr = (typeof arg) == 'object' ? JSON.stringify(arg):arg+""; const request = { action: 'executeContract', requestID, contractID, operation: method, arg: arg, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${method}|${argStr}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getSessionID(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getSessionID', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } listTheContractProcess(contractID: string): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'listTheContractProcess', requestID, contractID, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMask( contractID: string, // operation: string, ): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMask', requestID, contractID, // operation, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } setMask( contractID: string, operation: string, arg: string, ): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'setMask', requestID, contractID, operation, arg, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getMock( contractID: string, // operation: string, ): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getMock', requestID, contractID, // operation, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } setMock( contractID: string, operation: string, arg: string, ): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'setMock', requestID, contractID, operation, arg, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature( `${contractID}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } queryHashByOffset(offset: number, count: number): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'queryHashByOffset', requestID, offset, count, ...(sm2Key ? { pubkey: sm2Key.publicKey, /* signature: sm2.doSignature( id + '|' + sm2Key.publicKey, sm2Key.privateKey, { hash: true, der: true }, ), */ } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } loadNodeConfig(): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'loadNodeConfig', requestID, ...(sm2Key ? { pubkey: sm2Key.publicKey, signature: sm2.doSignature(sm2Key.publicKey, sm2Key.privateKey, { hash: true, der: true, }), } : {}), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } queryUserStat(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'queryUserStat', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } listNodes(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'listNodes', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } killContractProcess(id: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'killContractProcess', requestID, id, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } distributeYPK(projectName: string, nodeIDs: string): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'distributeYPK', requestID, pubKey: sm2Key.publicKey, projectName, nodeIDs, signature: sm2.doSignature( `DistributeYPK|${projectName}|${sm2Key.publicKey}`, sm2Key.privateKey, { hash: true, der: true }, ), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } listYPKs(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'listYPKs', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } deleteFile(file: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'deleteFile', requestID, file, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } startContractByYPK(project: string): Promise { const sm2Key = this.sm2Key const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'startContractByYPK', isPrivate: true, requestID, owner: sm2Key.publicKey, path: `/${project}`, signature: sm2.doSignature( `Fixed|${project}|${sm2Key.publicKey}`, sm2Key.privateKey, ), } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } initBDServer( host: string, username: string, password: string, name: string, clusterHost: string, ): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'initBDServer', requestID, host, username, password, name, sm2Key: JSON.stringify(this.sm2Key), clusterHost, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } initBDCluster( host: string, username: string, password: string, name: string, sm2Key: string, agents: [], ): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'initBDCluster', requestID, host, username, password, name, sm2Key, agents, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } listCompiledFiles(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'listCompiledFiles', requestID, isPrivate: true, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getManagerPubkey(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getManagerPubkey', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } getClusterName(): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'getClusterName', requestID, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } setClusterName(name: string): Promise { const requestID = `${new Date().getTime()}_${Math.floor( Math.random() * 10000, )}` const request = { action: 'setClusterName', requestID, name, } this.wssocket.send(JSON.stringify(request)) return new Promise((resolve, reject) => { this.promiseCallbackPairs[requestID] = { resolve, reject } }) } }