diff --git a/package.json b/package.json index 31ce9c1e2..e868bf18a 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@types/crypto-js": "^4.1.1", "@types/lodash": "^4.14.191", "@types/marked": "^4.0.1", + "@types/nprogress": "^0.2.3", "@types/sortablejs": "^1.10.7", "@types/utf8": "^3.0.1", "@typescript-eslint/eslint-plugin": "^7.18.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55a7a8c36..b7791e966 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@types/marked': specifier: ^4.0.1 version: 4.3.2 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 '@types/sortablejs': specifier: ^1.10.7 version: 1.15.8 @@ -968,6 +971,9 @@ packages: '@types/node@22.5.4': resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + '@types/sortablejs@1.15.8': resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} @@ -4131,6 +4137,8 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/nprogress@0.2.3': {} + '@types/sortablejs@1.15.8': {} '@types/urijs@1.19.25': {} diff --git a/src/common/http.js b/src/common/http.ts similarity index 76% rename from src/common/http.js rename to src/common/http.ts index 6cc4576c2..c22b1079b 100644 --- a/src/common/http.js +++ b/src/common/http.ts @@ -1,19 +1,31 @@ -import axios from 'axios' -import { ElNotification } from 'element-plus' +import { API_BASE_URL, REQUEST_TIMEOUT_CODE } from '@/common/constants' +import { BAD_TOKEN, NAME_PWD_ERROR, TOKEN_TIME_OUT } from '@/common/customErrorCode' import CustomMessage from '@/common/CustomMessage' -import NProgress from 'nprogress' -import 'nprogress/nprogress.css' +import i18n from '@/i18n' import { toLogin } from '@/router' import store from '@/store' import { stringifyObjSafely } from '@emqx/shared-ui-utils' -import _ from 'lodash' -import { API_BASE_URL, REQUEST_TIMEOUT_CODE } from '@/common/constants' -import { BAD_TOKEN, TOKEN_TIME_OUT, NAME_PWD_ERROR } from '@/common/customErrorCode' -import i18n from '@/i18n' +import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios' +import axios from 'axios' +import { ElNotification } from 'element-plus' +import { throttle } from 'lodash' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' + +type CustomRequestConfig = InternalAxiosRequestConfig & { + doNotTriggerProgress?: boolean + errorsHandleCustom?: number[] + handleTimeoutSelf?: boolean + controller?: AbortController +} + +type CustomResponse = AxiosResponse & { + config: CustomRequestConfig +} NProgress.configure({ showSpinner: false, trickleSpeed: 200 }) -let respSet = new Set() -const resetRespSet = () => (respSet = new Set()) +let respSet = new Set() +const resetRespSet = () => (respSet = new Set()) Object.assign(axios.defaults, { baseURL: API_BASE_URL, @@ -21,11 +33,9 @@ Object.assign(axios.defaults, { }) axios.interceptors.request.use( - (config) => { + (config: CustomRequestConfig) => { const { user } = store.state - config.headers = { - Authorization: 'Bearer ' + user.token, - } + config.headers.Authorization = 'Bearer ' + user.token const controller = new AbortController() config.signal = controller.signal config.controller = controller @@ -33,11 +43,11 @@ axios.interceptors.request.use( return config }, (error) => { - Promise.reject(error) + return Promise.reject(error) }, ) -axios.interceptors.request.use(async (config) => { +axios.interceptors.request.use(async (config: CustomRequestConfig) => { if (!config.doNotTriggerProgress) { if (!store.state.request_queue) { NProgress.start() @@ -47,10 +57,10 @@ axios.interceptors.request.use(async (config) => { return config }) -const isTokenExpired = (status, data) => +const isTokenExpired = (status: number, data: any) => status === 401 && [BAD_TOKEN, TOKEN_TIME_OUT].includes(data.code) -const readBlobResponse = async (data) => { +const readBlobResponse = async (data: Blob) => { try { const ret = await data.text() return JSON.parse(ret) @@ -59,7 +69,7 @@ const readBlobResponse = async (data) => { } } -const getErrorMessage = (data, status) => { +const getErrorMessage = (data: AxiosResponse['data'], status: number) => { if (!data) { return `${status} Network error` } @@ -85,8 +95,8 @@ const getErrorMessage = (data, status) => { * handleTimeoutSelf: when error.code === 'ECONNABORTED', handle the error if self */ axios.interceptors.response.use( - (response) => { - if (!response.config?.doNotTriggerProgress) { + (response: CustomResponse) => { + if (!response.config.doNotTriggerProgress) { setProgressBarDone() } if (response.data instanceof Blob) { @@ -98,11 +108,12 @@ axios.interceptors.response.use( store.commit('REMOVE_ABORT_CONTROLLER', controller) return response.data || response.status }, - async (error) => { + async (error: any) => { if (!error.config?.doNotTriggerProgress) { setProgressBarDone() } + const t: (key: string) => string = (i18n.global as any).t //throttle concurrent responses with unique status code if (error.response) { if (error.response.data instanceof Blob) { @@ -119,7 +130,7 @@ axios.interceptors.response.use( if (doNotPopupAfterPwdChanged) { store.commit('SET_AFTER_CURRENT_USER_PWD_CHANGED', false) } else { - ElNotification.error(i18n.global.t('Base.tokenExpiredMsg')) + ElNotification.error(t('Base.tokenExpiredMsg')) } toLogin() // reset set, otherwise will not popup error msg @@ -133,7 +144,7 @@ axios.interceptors.response.use( error.config.errorsHandleCustom.includes(status) if (!handleErrorSelf) { if (data.code === NAME_PWD_ERROR) { - ElNotification.error(i18n.global.t('Base.namePwdError')) + ElNotification.error(t('Base.namePwdError')) } else { CustomMessage.error(getErrorMessage(data, status)) } @@ -150,14 +161,16 @@ axios.interceptors.response.use( } if (!respSet.has(0)) { if (!doNotPopupError) { - CustomMessage.error(i18n.global.t('Base.networkError')) + CustomMessage.error(t('Base.networkError')) } respSet.add(0) } } - if (store.state.request_queue === 0) respSet = new Set() - _.throttle(resetRespSet, 2000, { trailing: false }) + if (store.state.request_queue === 0) { + respSet = new Set() + } + throttle(resetRespSet, 2000, { trailing: false })() // Remove AbortController const controller = error.config.controller store.commit('REMOVE_ABORT_CONTROLLER', controller)