diff --git a/README.md b/README.md index 6402e15..ff8c2f9 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ BDContract SDK for Node.js and browsers. `npm install @bdware/bdcontract-sdk` ```typescript -import { Client } from '@bdware/bdcontract-sdk' +import { WsClient } from '@bdware/bdcontract-sdk' import { sm2 } from 'sm-crypto' -const client = new Client( +const client = new WsClient( url, (ev) => { console.log(JSON.stringify(ev)) @@ -42,7 +42,7 @@ function print(string) { const url = 'ws://021.node.internetapi.cn:21030/SCIDE/SCExecutor' -const client = new bdcontract.Client( +const client = new bdcontract.WsClient( url, (ev) => { console.log(JSON.stringify(ev)) @@ -76,4 +76,4 @@ async function anotherPlace() { // In another place, wait for login to complete const success = await client.Loggedin() }() -``` +``` \ No newline at end of file diff --git a/package.json b/package.json index 7490d08..2334ea3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bdware/bdcontract-sdk", - "version": "0.3.0-alpha.15", + "version": "0.4.0-beta", "description": "BDContract SDK for Node.js and browsers", "license": "MIT", "keywords": [ @@ -47,14 +47,5 @@ "shx": "^0.3.4", "sucrase": "^3.25.0", "typescript": "^4.8.2" - }, - "directories": { - "lib": "lib", - "test": "test" - }, - "repository": { - "type": "git", - "url": "git@gitea.internetapi.cn:bdware/bdcontract-sdk-javascript.git" - }, - "author": "caihq@pku.edu.cn" + } } diff --git a/src/httpClient.ts b/src/httpClient.ts index 5107ca6..27bcdb9 100644 --- a/src/httpClient.ts +++ b/src/httpClient.ts @@ -26,19 +26,40 @@ const genUrlParamsFromObject = (obj: Record): string => { export * from './types' +export const retryAsyncN = + (maxRetries: number) => + async (fn: () => Promise) => { + let i = 0 + while (true) + try { + return await fn() + } catch (e) { + if (i++ === maxRetries) { + throw e + } + } + } + export class HttpClient { private fetch: (input: RequestInfo, init?: RequestInit) => Promise + private retryAsync: (fn: () => Promise) => Promise constructor( private baseUrl: string, private sm2Key: KeyPairHex, - config = {} as AxiosRequestConfig, + options: { + axios: AxiosRequestConfig + maxRetries: number + } = { + axios: {}, + maxRetries: 3, + }, ) { const axiosInstance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 10000, // headers: {'X-Custom-Header': 'foobar'} - ...config, + ...options.axios, }) this.fetch = buildAxiosFetch( @@ -47,6 +68,8 @@ export class HttpClient { return config; } */, ) + + this.retryAsync = retryAsyncN(options.maxRetries) } async requestWithSignature( @@ -107,7 +130,6 @@ export class HttpClient { .catch((err) => { // const clientRes = err as unknown as ClientResponse // clientRes.data = (await err.text()) as Data - // console.log(err,'errerrerrerrerrerrerrerrerrerrerrerrerrerrerr') setTimeout(() => { reject(err) }, 1) @@ -115,22 +137,6 @@ export class HttpClient { }) } - async retryRequestWithSignature(retryTimes: number, path: string, init?: Partial, sm2Key?: KeyPairHex ) { - let err: Error = new Error() - for (let i = 0; i(path, init, sm2Key) - } catch (e) { - if (i < retryTimes) { - console.log('1') - continue - } - err = e as Error - } - } - throw err - } - sign(data: string, privateKey?: string): string { return sm2.doSignature(data, privateKey ?? this.sm2Key.privateKey, { hash: true, @@ -141,15 +147,17 @@ export class HttpClient { // ping // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id15 ping(): Promise { - return this.requestWithSignature('/SCManager?action=ping') + return this.retryAsync(() => + this.requestWithSignature('/SCManager?action=ping'), + ) } // 启动合约 // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id60 startContract(code: string): Promise> { const params = { action: 'startContract', script: code } - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject(params)}`, + return this.retryAsync(() => + this.requestWithSignature(`/SCManager?${genUrlParamsFromObject(params)}`), ) } @@ -158,21 +166,23 @@ export class HttpClient { startContractByYPK( _request: StartContractByYpkRequest, ): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'startContractByYPK', - ..._request, - owner: this.sm2Key.publicKey, - aim: 'onStartContract', - signature: sm2.doSignature( - `Fixed|${_request.path}|${this.sm2Key.publicKey}`, - this.sm2Key.privateKey, - { - hash: true, - der: true, - }, - ), - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'startContractByYPK', + ..._request, + owner: this.sm2Key.publicKey, + aim: 'onStartContract', + signature: sm2.doSignature( + `Fixed|${_request.path}|${this.sm2Key.publicKey}`, + this.sm2Key.privateKey, + { + hash: true, + der: true, + }, + ), + })}`, + ), ) } @@ -214,17 +224,17 @@ export class HttpClient { ), } } - - return this.retryRequestWithSignature( - 3, - `/SCManager${ - method === 'GET' ? `?${genUrlParamsFromObject(request)}` : '' - }`, - { - method, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - ...(method === 'POST' ? ({ body: request } as Request) : {}), - }, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager${ + method === 'GET' ? `?${genUrlParamsFromObject(request)}` : '' + }`, + { + method, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + ...(method === 'POST' ? ({ body: request } as Request) : {}), + }, + ), ) } @@ -239,8 +249,10 @@ export class HttpClient { id: contractID, } requestID && (_request.requestID = requestID!) - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject(_request)}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject(_request)}`, + ), ) } @@ -248,7 +260,9 @@ export class HttpClient { // 地址 http://39.104.205.122:18010/SCIDE // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id131 killAllContract(): Promise> { - return this.requestWithSignature('/SCManager?action=killAllContract') + return this.retryAsync(() => + this.requestWithSignature('/SCManager?action=killAllContract'), + ) } // 申请角色 @@ -260,8 +274,10 @@ export class HttpClient { action: 'applyNodeRole', role, } - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject(_request)}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject(_request)}`, + ), ) } @@ -277,10 +293,12 @@ export class HttpClient { isAccept, authorizedPubKey, } - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject(_request)}`, - undefined, - managerPair, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject(_request)}`, + undefined, + managerPair, + ), ) } @@ -332,22 +350,26 @@ export class HttpClient { } saveFile(_request: SaveFileRequest): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'saveFile', - ..._request, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'saveFile', + ..._request, + })}`, + ), ) } listProjectPermission( _request: ListProjectPermissionRequest, ): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'listProjectPermission', - ..._request, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'listProjectPermission', + ..._request, + })}`, + ), ) } @@ -368,22 +390,31 @@ export class HttpClient { isPrivate, sponsorPeerID, } - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'startContractMultiPoint', - ..._request, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'startContractMultiPoint', + ..._request, + })}`, + ), ) } // 获取节点配置信息 async loadNodeConfig(): Promise> { - const res = await this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'loadNodeConfig', - })}`, + const res = await this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'loadNodeConfig', + })}`, + ), ) - return { ...res, data: (res.status === 200 && res.data ? JSON.parse( res.data).data : {}) as LoadNodeConfigResponseData} + return { + ...res, + data: (res.status === 200 && res.data + ? JSON.parse(res.data).data + : {}) as LoadNodeConfigResponseData, + } } // 支持的key包括:{licence,projectDir,yjsPath,dataChain,doipConfig,nodeCenter,nodeName,masterAddress,resetNodeCenterWS} @@ -391,21 +422,25 @@ export class HttpClient { key: string, val: string, ): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'updateConfig', - key, - val, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'updateConfig', + key, + val, + })}`, + ), ) } // 设置pubkey为node manager async resetNodeManager(): Promise { - const res = await this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'resetNodeManager', - })}`, + const res = await this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'resetNodeManager', + })}`, + ), ) if (!res.data) { return false @@ -416,38 +451,46 @@ export class HttpClient { // 锁定某个用户的的私有目录编辑功能 lockEdit(): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'lockEdit', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'lockEdit', + })}`, + ), ) } // 解锁某个用户的的私有目录编辑功能 unlockEdit(): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'unlockEdit', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'unlockEdit', + })}`, + ), ) } addNode(nodePubKey: string): Promise> { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'addNode', - nodePubKey, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'addNode', + nodePubKey, + })}`, + ), ) } // 申请角色 applyRole(role: string): Promise> { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'applyRole', - role, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'applyRole', + role, + })}`, + ), ) } @@ -455,29 +498,35 @@ export class HttpClient { isAccept: boolean, authorizedPubKey: string, ): Promise> { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'authNodeManager', - isAccept, - authorizedPubKey, - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'authNodeManager', + isAccept, + authorizedPubKey, + })}`, + ), ) } listAllUsers(): Promise> { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'listAllUsers', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'listAllUsers', + })}`, + ), ) } // 查看节点列表 listNodes(): Promise { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'listNodes', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'listNodes', + })}`, + ), ) as unknown as Promise } @@ -502,30 +551,36 @@ export class HttpClient { '04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27', sign: sig, } - return (await this.requestWithSignature('', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - })) as unknown as { action: string; status: string } + return (await this.retryAsync(() => + this.requestWithSignature('', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }), + )) as unknown as { action: string; status: string } } // 查看可信执行集群列表 listTrustUnits(): Promise> { - return this.requestWithSignature( - `?${genUrlParamsFromObject({ - action: 'listTrustUnits', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `?${genUrlParamsFromObject({ + action: 'listTrustUnits', + })}`, + ), ) } // listContractProcess 查询合约实例列表 listContractProcess(): Promise> { - return this.requestWithSignature( - `/SCManager?${genUrlParamsFromObject({ - action: 'listContractProcess', - })}`, + return this.retryAsync(() => + this.requestWithSignature( + `/SCManager?${genUrlParamsFromObject({ + action: 'listContractProcess', + })}`, + ), ) } @@ -533,7 +588,7 @@ export class HttpClient { downloadContract( projectName: string, isPrivate: boolean, - timestamp: number + timestamp: number, ): Promise> { const _request = { action: 'downloadContract', @@ -541,19 +596,22 @@ export class HttpClient { isPrivate, timestamp, } - return this.requestWithSignature( - `/CMManager?${genUrlParamsFromObject(_request)}`) + return this.retryAsync(() => + this.requestWithSignature( + `/CMManager?${genUrlParamsFromObject(_request)}`, + ), + ) } // 配置合约引擎节点,若节点没有设置过node manager,将当前key设置为node manager - async configNode( - args: ConfigNodeArgs, - ): Promise { + async configNode(args: ConfigNodeArgs): Promise { if (!(await this.resetNodeManager())) { return false } - const kvs: [string, string][] = Object.entries(args).filter(([_k, v]) => v) as [string, string][] + const kvs: [string, string][] = Object.entries(args).filter( + ([_k, v]) => v, + ) as [string, string][] await Promise.all([ ...kvs.map(([k, v]) => this.updateConfig(k, v!)),