api-gen-cli
Advanced tools
+1
-1
| { | ||
| "name": "api-gen-cli", | ||
| "version": "0.0.13", | ||
| "version": "0.0.14", | ||
| "description": "api-gw", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
+213
-213
| import Axios, { AxiosInstance, AxiosRequestConfig } from 'axios' | ||
| import Qs = require('qs') | ||
| import MD5 = require('js-md5') | ||
| import EventEmitter = require('eventemitter3') | ||
| import Qs from 'qs' | ||
| import MD5 from 'js-md5' | ||
| import EventEmitter from 'eventemitter3' | ||
@@ -18,25 +18,25 @@ //FOR release | ||
| export class ApiError { | ||
| code: number; | ||
| domain: string; | ||
| message: string; | ||
| error?: Error | any; | ||
| code: number; | ||
| domain: string; | ||
| message: string; | ||
| error?: Error | any; | ||
| constructor(code?: number, msg?: string, domain?: string) { | ||
| this.code = code || -1; | ||
| this.domain = domain || 'unknown'; | ||
| this.message = msg || '未知异常'; | ||
| } | ||
| constructor(code?: number, msg?: string, domain?: string) { | ||
| this.code = code || -1; | ||
| this.domain = domain || 'unknown'; | ||
| this.message = msg || '未知异常'; | ||
| } | ||
| withError(e?: Error | any): this { | ||
| this.error = e; | ||
| return this; | ||
| } | ||
| withError(e?: Error | any): this { | ||
| this.error = e; | ||
| return this; | ||
| } | ||
| isBusinessError() { | ||
| return this.domain === DOMIN_BIZ; | ||
| } | ||
| isBusinessError() { | ||
| return this.domain === DOMIN_BIZ; | ||
| } | ||
| isHttpError() { | ||
| return this.domain === DOMIN_HTTP; | ||
| } | ||
| isHttpError() { | ||
| return this.domain === DOMIN_HTTP; | ||
| } | ||
| } | ||
@@ -55,225 +55,225 @@ | ||
| class HTTPClient { | ||
| private _tokenInfo: TokenInfo | null = null | ||
| private get tokenInfo() { | ||
| if (!this._tokenInfo) { | ||
| this._tokenInfo = { | ||
| token: localStorage.getItem(K_LOCAL_TOKEN) || '', | ||
| secret: localStorage.getItem(K_LOCAL_SECRET) || '', | ||
| refreshToken: localStorage.getItem(K_LOCAL_REFRESH_TOKEN) || '' | ||
| } | ||
| } | ||
| return this._tokenInfo | ||
| private _tokenInfo: TokenInfo | null = null | ||
| private get tokenInfo() { | ||
| if (!this._tokenInfo) { | ||
| this._tokenInfo = { | ||
| token: localStorage.getItem(K_LOCAL_TOKEN) || '', | ||
| secret: localStorage.getItem(K_LOCAL_SECRET) || '', | ||
| refreshToken: localStorage.getItem(K_LOCAL_REFRESH_TOKEN) || '' | ||
| } | ||
| } | ||
| private set tokenInfo(tokenInfo: TokenInfo | null) { | ||
| this._tokenInfo = tokenInfo | ||
| localStorage.removeItem(K_LOCAL_TOKEN) | ||
| localStorage.removeItem(K_LOCAL_SECRET) | ||
| localStorage.removeItem(K_LOCAL_REFRESH_TOKEN) | ||
| if (tokenInfo) { | ||
| tokenInfo.token && localStorage.setItem(K_LOCAL_TOKEN, tokenInfo.token) | ||
| tokenInfo.secret && localStorage.setItem(K_LOCAL_SECRET, tokenInfo.secret); | ||
| tokenInfo.refreshToken && localStorage.setItem(K_LOCAL_REFRESH_TOKEN, tokenInfo.refreshToken); | ||
| } | ||
| return this._tokenInfo | ||
| } | ||
| private set tokenInfo(tokenInfo: TokenInfo | null) { | ||
| this._tokenInfo = tokenInfo | ||
| localStorage.removeItem(K_LOCAL_TOKEN) | ||
| localStorage.removeItem(K_LOCAL_SECRET) | ||
| localStorage.removeItem(K_LOCAL_REFRESH_TOKEN) | ||
| if (tokenInfo) { | ||
| tokenInfo.token && localStorage.setItem(K_LOCAL_TOKEN, tokenInfo.token) | ||
| tokenInfo.secret && localStorage.setItem(K_LOCAL_SECRET, tokenInfo.secret); | ||
| tokenInfo.refreshToken && localStorage.setItem(K_LOCAL_REFRESH_TOKEN, tokenInfo.refreshToken); | ||
| } | ||
| } | ||
| private isTokenRefreshing = false //正在刷新token | ||
| private eventEmitter = new EventEmitter() //因为刷新token而被block的request | ||
| private isTokenRefreshing = false //正在刷新token | ||
| private eventEmitter = new EventEmitter() //因为刷新token而被block的request | ||
| private instance?: AxiosInstance | ||
| private get client(): AxiosInstance { | ||
| if (!this.instance) { | ||
| this.instance = Axios.create({ | ||
| headers: { | ||
| 'Content-Type': 'application/json;charset=utf-8' | ||
| }, | ||
| timeout: 30000, | ||
| paramsSerializer: (params) => { | ||
| return Qs.stringify(params) | ||
| } | ||
| }) | ||
| private instance?: AxiosInstance | ||
| private get client(): AxiosInstance { | ||
| if (!this.instance) { | ||
| this.instance = Axios.create({ | ||
| headers: { | ||
| 'Content-Type': 'application/json;charset=utf-8' | ||
| }, | ||
| timeout: 30000, | ||
| paramsSerializer: (params) => { | ||
| return Qs.stringify(params) | ||
| } | ||
| }) | ||
| this.instance.interceptors.request.use(config => { | ||
| config.baseURL = this.baseURL | ||
| const token = this.tokenInfo?.token | ||
| if (token) { | ||
| config.headers[CommonParameter.token] = token | ||
| } | ||
| this.instance.interceptors.request.use(config => { | ||
| config.baseURL = this.baseURL | ||
| const token = this.tokenInfo?.token | ||
| if (token) { | ||
| config.headers[CommonParameter.token] = token | ||
| } | ||
| return config | ||
| }, error => { | ||
| return Promise.reject(new ApiError(-1, '请求错误,请重试', DOMIN_HTTP).withError(error)); | ||
| }) | ||
| return config | ||
| }, error => { | ||
| return Promise.reject(new ApiError(-1, '请求错误,请重试', DOMIN_HTTP).withError(error)); | ||
| }) | ||
| this.instance.interceptors.response.use(response => { | ||
| const { code, msg, domain } = response.data | ||
| if (code !== 0) { | ||
| return Promise.reject(new ApiError(code, msg, domain)) | ||
| } | ||
| return response | ||
| }, error => { | ||
| return Promise.reject(new ApiError(-1, '网络异常,请重试', DOMIN_HTTP).withError(error)) | ||
| }) | ||
| this.instance.interceptors.response.use(response => { | ||
| const { code, msg, domain } = response.data | ||
| if (code !== 0) { | ||
| return Promise.reject(new ApiError(code, msg, domain)) | ||
| } | ||
| return this.instance | ||
| return response | ||
| }, error => { | ||
| return Promise.reject(new ApiError(-1, '网络异常,请重试', DOMIN_HTTP).withError(error)) | ||
| }) | ||
| } | ||
| private signParams(params: Params, sm: string, security?: SECURITY): string { | ||
| let salt = DEFAULT_SECURT | ||
| if (security !== 'NONE') { | ||
| salt = this.tokenInfo?.secret || DEFAULT_SECURT | ||
| } | ||
| const raw = `${Qs.stringify(params)}${salt}` | ||
| return this.sign(raw, sm) | ||
| return this.instance | ||
| } | ||
| private signParams(params: Params, sm: string, security?: SECURITY): string { | ||
| let salt = DEFAULT_SECURT | ||
| if (security !== 'NONE') { | ||
| salt = this.tokenInfo?.secret || DEFAULT_SECURT | ||
| } | ||
| const raw = `${Qs.stringify(params)}${salt}` | ||
| return this.sign(raw, sm) | ||
| } | ||
| /** | ||
| * | ||
| * @param raw 原始字符串 | ||
| * @param sm 签名算法 hmac,md5,sha1,rsa,ecc | ||
| */ | ||
| private sign(raw: string, sm: string): string { | ||
| if (sm === 'sha1') { | ||
| //TODO | ||
| } | ||
| return MD5(raw) | ||
| /** | ||
| * | ||
| * @param raw 原始字符串 | ||
| * @param sm 签名算法 hmac,md5,sha1,rsa,ecc | ||
| */ | ||
| private sign(raw: string, sm: string): string { | ||
| if (sm === 'sha1') { | ||
| //TODO | ||
| } | ||
| private processParams(params?: Params, security?: SECURITY): Params { | ||
| const timestamp = new Date().getTime() | ||
| const sm = 'md5' | ||
| return MD5(raw) | ||
| } | ||
| const _params: Params = { | ||
| timestamp, | ||
| [CommonParameter.signatureMethod]: sm, | ||
| ...(params || {}) | ||
| } | ||
| _params[CommonParameter.signature] = this.signParams(_params, sm, security) | ||
| return _params | ||
| private processParams(params?: Params, security?: SECURITY): Params { | ||
| const timestamp = new Date().getTime() | ||
| const sm = 'md5' | ||
| const _params: Params = { | ||
| timestamp, | ||
| [CommonParameter.signatureMethod]: sm, | ||
| ...(params || {}) | ||
| } | ||
| _params[CommonParameter.signature] = this.signParams(_params, sm, security) | ||
| return _params | ||
| } | ||
| /** | ||
| * 刷新token | ||
| * @param config axios config | ||
| */ | ||
| private async waitingRefreshToken<T>(config: AxiosRequestConfig) { | ||
| let promise: Promise<T>; | ||
| if (this.isTokenRefreshing) { | ||
| promise = new Promise<T>((resovle, reject) => { | ||
| this.eventEmitter.once(EVENT_REFRESH_TOKEN, (e: ApiError) => { | ||
| if (e) reject(e) | ||
| else resovle(this.request<T>(config)) | ||
| }) | ||
| }) | ||
| } else { | ||
| this.isTokenRefreshing = true | ||
| try { | ||
| const refreshToken = this.tokenInfo?.refreshToken || '' | ||
| const config = { ...REFRESH_TOKEN_API_CONFIG } as AxiosRequestConfig | ||
| config.method = 'post' | ||
| config.data = this.processParams({ | ||
| [CommonParameter.refreshToken]: refreshToken, | ||
| ...(config.data || {}) | ||
| }, 'USER_LOGIN') | ||
| /** | ||
| * 刷新token | ||
| * @param config axios config | ||
| */ | ||
| private async waitingRefreshToken<T>(config: AxiosRequestConfig) { | ||
| let promise: Promise<T>; | ||
| if (this.isTokenRefreshing) { | ||
| promise = new Promise<T>((resovle, reject) => { | ||
| this.eventEmitter.once(EVENT_REFRESH_TOKEN, (e: ApiError) => { | ||
| if (e) reject(e) | ||
| else resovle(this.request<T>(config)) | ||
| }) | ||
| }) | ||
| } else { | ||
| this.isTokenRefreshing = true | ||
| try { | ||
| const refreshToken = this.tokenInfo?.refreshToken || '' | ||
| const config = { ...REFRESH_TOKEN_API_CONFIG } as AxiosRequestConfig | ||
| config.method = 'post' | ||
| config.data = this.processParams({ | ||
| [CommonParameter.refreshToken]: refreshToken, | ||
| ...(config.data || {}) | ||
| }, 'USER_LOGIN') | ||
| const rsp = await this.client.request(config) | ||
| this.tokenInfo = rsp.data.contents | ||
| this.isTokenRefreshing = false//刷新成功 | ||
| setTimeout(() => { | ||
| this.eventEmitter.emit(EVENT_REFRESH_TOKEN) | ||
| }, 0); | ||
| return this.request<T>(config) | ||
| } catch (e) { | ||
| this.isTokenRefreshing = false//刷新失败 | ||
| this.eventEmitter.emit(EVENT_REFRESH_TOKEN, e) | ||
| promise = Promise.reject(e) | ||
| } | ||
| } | ||
| return promise.catch(e => { | ||
| if (!(e instanceof ApiError)) { | ||
| e = new ApiError().withError(e) | ||
| } | ||
| const apiError = e as ApiError | ||
| return Promise.reject(apiError) | ||
| }) | ||
| const rsp = await this.client.request(config) | ||
| this.tokenInfo = rsp.data.contents | ||
| this.isTokenRefreshing = false//刷新成功 | ||
| setTimeout(() => { | ||
| this.eventEmitter.emit(EVENT_REFRESH_TOKEN) | ||
| }, 0); | ||
| return this.request<T>(config) | ||
| } catch (e) { | ||
| this.isTokenRefreshing = false//刷新失败 | ||
| this.eventEmitter.emit(EVENT_REFRESH_TOKEN, e) | ||
| promise = Promise.reject(e) | ||
| } | ||
| } | ||
| /** | ||
| * base request | ||
| * @param config | ||
| */ | ||
| private request = async <T>(config: AxiosRequestConfig): Promise<T> => { | ||
| if (this.isTokenRefreshing) return this.waitingRefreshToken<T>(config) | ||
| return promise.catch(e => { | ||
| if (!(e instanceof ApiError)) { | ||
| e = new ApiError().withError(e) | ||
| } | ||
| const apiError = e as ApiError | ||
| return Promise.reject(apiError) | ||
| }) | ||
| } | ||
| return this.client.request(config).then(rsp => { | ||
| const rt = rsp.data.contents | ||
| if (isTokenInfo(rt)) { | ||
| this.tokenInfo = rt | ||
| } | ||
| return rt | ||
| }).catch(e => { | ||
| if (!(e instanceof ApiError)) { | ||
| e = new ApiError().withError(e) | ||
| } | ||
| /** | ||
| * base request | ||
| * @param config | ||
| */ | ||
| private request = async <T>(config: AxiosRequestConfig): Promise<T> => { | ||
| if (this.isTokenRefreshing) return this.waitingRefreshToken<T>(config) | ||
| //refresh token | ||
| const apiError = e as ApiError | ||
| if (apiError.domain === 'API' && apiError.code === 406 && REFRESH_TOKEN_API_CONFIG) { | ||
| return this.waitingRefreshToken<T>(config) | ||
| } | ||
| return this.client.request(config).then(rsp => { | ||
| const rt = rsp.data.contents | ||
| if (isTokenInfo(rt)) { | ||
| this.tokenInfo = rt | ||
| } | ||
| return rt | ||
| }).catch(e => { | ||
| if (!(e instanceof ApiError)) { | ||
| e = new ApiError().withError(e) | ||
| } | ||
| return Promise.reject(apiError) | ||
| }) | ||
| } | ||
| //refresh token | ||
| const apiError = e as ApiError | ||
| if (apiError.domain === 'API' && apiError.code === 406 && REFRESH_TOKEN_API_CONFIG) { | ||
| return this.waitingRefreshToken<T>(config) | ||
| } | ||
| /** | ||
| * GET 请求 | ||
| * @param url | ||
| * @param params | ||
| * @param security API安全等级 | ||
| */ | ||
| GET = <T = any>(url?: string, params?: Params, security?: SECURITY): Promise<T> => { | ||
| return this.request({ | ||
| method: 'get', | ||
| url, | ||
| params: this.processParams(params, security), | ||
| }) | ||
| } | ||
| return Promise.reject(apiError) | ||
| }) | ||
| } | ||
| /** | ||
| * POST 请求 | ||
| * @param url | ||
| * @param data | ||
| * @param security API安全等级 | ||
| */ | ||
| POST = <T = any>(url?: string, data?: Params, security?: SECURITY): Promise<T> => { | ||
| return this.request({ | ||
| method: 'post', | ||
| url, | ||
| data: this.processParams(data, security) | ||
| }) | ||
| } | ||
| /** | ||
| * GET 请求 | ||
| * @param url | ||
| * @param params | ||
| * @param security API安全等级 | ||
| */ | ||
| GET = <T = any>(url?: string, params?: Params, security?: SECURITY): Promise<T> => { | ||
| return this.request({ | ||
| method: 'get', | ||
| url, | ||
| params: this.processParams(params, security), | ||
| }) | ||
| } | ||
| /** | ||
| * base url | ||
| */ | ||
| baseURL?: string | ||
| /** | ||
| * POST 请求 | ||
| * @param url | ||
| * @param data | ||
| * @param security API安全等级 | ||
| */ | ||
| POST = <T = any>(url?: string, data?: Params, security?: SECURITY): Promise<T> => { | ||
| return this.request({ | ||
| method: 'post', | ||
| url, | ||
| data: this.processParams(data, security) | ||
| }) | ||
| } | ||
| /** | ||
| * 清除token等相关缓存信息 | ||
| */ | ||
| clean() { | ||
| this.tokenInfo = null | ||
| } | ||
| /** | ||
| * base url | ||
| */ | ||
| baseURL?: string | ||
| get isLogin() { | ||
| return !!this.tokenInfo.token; | ||
| } | ||
| /** | ||
| * 清除token等相关缓存信息 | ||
| */ | ||
| clean() { | ||
| this.tokenInfo = null | ||
| } | ||
| get isLogin() { | ||
| return !!this.tokenInfo?.token; | ||
| } | ||
| } | ||
| export const HTTP = new HTTPClient() |
24792
-3.99%