diff --git a/.gitignore b/.gitignore index 21160fe..ee14ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ .idea/ dist/ .vercel +test/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 650149e..2c8a8f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clashofclans.js", - "version": "2.9.1", + "version": "2.9.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "clashofclans.js", - "version": "2.9.1", + "version": "2.9.2", "license": "MIT", "dependencies": { "node-fetch": "^2.6.7" diff --git a/package.json b/package.json index 54f6cba..39c0db4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clashofclans.js", - "version": "2.9.1", + "version": "2.9.2", "description": "JavaScript library for interacting with the Clash of Clans API", "author": "SUVAJIT ", "license": "MIT", diff --git a/src/rest/RequestHandler.ts b/src/rest/RequestHandler.ts index fc288fa..6c49486 100644 --- a/src/rest/RequestHandler.ts +++ b/src/rest/RequestHandler.ts @@ -1,5 +1,5 @@ import https from 'https'; -import fetch from 'node-fetch'; +import fetch, { FetchError } from 'node-fetch'; import { HTTPError, PrivateWarLogError } from './HTTPError'; import { QueueThrottler, BatchThrottler } from './Throttler'; import { Response, RequestOptions, LoginOptions, Store, RequestHandlerOptions } from '../types'; @@ -71,40 +71,50 @@ export class RequestHandler { } private async exec(path: string, options: RequestOptions = {}, retries = 0): Promise> { - const res = await fetch(`${this.baseURL}${path}`, { - agent, - body: options.body, - method: options.method, - timeout: options.restRequestTimeout ?? this.restRequestTimeout, - headers: { 'Authorization': `Bearer ${this._key}`, 'Content-Type': 'application/json' } - }).catch(() => null); - - const data = await res?.json().catch(() => null); - if (!res && retries < (options.retryLimit ?? this.retryLimit)) return this.exec(path, options, ++retries); - - if ( - this.creds && - res?.status === 403 && - data?.reason === 'accessDenied.invalidIp' && - retries < (options.retryLimit ?? this.retryLimit) - ) { - const keys = await this.reValidateKeys().then(() => () => this.login()); - if (keys.length) return this.exec(path, options, ++retries); - } + try { + const res = await fetch(`${this.baseURL}${path}`, { + agent, + body: options.body, + method: options.method, + timeout: options.restRequestTimeout ?? this.restRequestTimeout, + headers: { 'Authorization': `Bearer ${this._key}`, 'Content-Type': 'application/json' } + }); + + if (res.status === 504 && retries < (options.retryLimit ?? this.retryLimit)) { + return await this.exec(path, options, ++retries); + } + const data = await res.json(); + + if ( + this.creds && + res.status === 403 && + data.reason === 'accessDenied.invalidIp' && + retries < (options.retryLimit ?? this.retryLimit) + ) { + const keys = await this.reValidateKeys().then(() => () => this.login()); + if (keys.length) return await this.exec(path, options, ++retries); + } - const maxAge = Number(res?.headers.get('cache-control')?.split('=')?.[1] ?? 0) * 1000; + const maxAge = Number(res.headers.get('cache-control')?.split('=')?.[1] ?? 0) * 1000; - if (res?.status === 403 && !data?.message && this.rejectIfNotValid) { - throw new HTTPError(PrivateWarLogError, res.status, path, maxAge); - } - if (!res?.ok && this.rejectIfNotValid) { - throw new HTTPError(data, res?.status ?? 504, path, maxAge, options.method); - } + if (res.status === 403 && !data?.message && this.rejectIfNotValid) { + throw new HTTPError(PrivateWarLogError, res.status, path, maxAge); + } + if (!res.ok && this.rejectIfNotValid) { + throw new HTTPError(data, res.status, path, maxAge, options.method); + } - if (this.cached && maxAge > 0 && options.cache !== false && res?.ok) { - await this.cached.set(path, { data, ttl: Date.now() + maxAge, status: res.status }, maxAge); + if (this.cached && maxAge > 0 && options.cache !== false && res.ok) { + await this.cached.set(path, { data, ttl: Date.now() + maxAge, status: res.status }, maxAge); + } + return { data, maxAge, status: res.status, path, ok: res.status === 200 }; + } catch (error) { + if (error instanceof FetchError && error.type === 'request-timeout' && retries < (options.retryLimit ?? this.retryLimit)) { + return this.exec(path, options, ++retries); + } + if (this.rejectIfNotValid) throw error; + return { data: { message: (error as FetchError).message } as unknown as T, maxAge: 0, status: 500, path, ok: false }; } - return { data, maxAge, status: res?.status ?? 504, path, ok: res?.status === 200 }; } public async init(options: LoginOptions) {