mirror of
https://github.com/langgenius/dify.git
synced 2026-04-26 18:27:15 +08:00
feat: refactor http client
This commit is contained in:
parent
ebdf72fffc
commit
d4f7ebfd2e
@ -64,6 +64,7 @@
|
|||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
|
"ky": "^1.7.2",
|
||||||
"lamejs": "^1.2.1",
|
"lamejs": "^1.2.1",
|
||||||
"lexical": "^0.18.0",
|
"lexical": "^0.18.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
@ -84,9 +85,9 @@
|
|||||||
"react-hook-form": "^7.53.1",
|
"react-hook-form": "^7.53.1",
|
||||||
"react-i18next": "^15.1.0",
|
"react-i18next": "^15.1.0",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
|
"react-markdown": "^9.0.1",
|
||||||
"react-multi-email": "^1.0.25",
|
"react-multi-email": "^1.0.25",
|
||||||
"react-papaparse": "^4.4.0",
|
"react-papaparse": "^4.4.0",
|
||||||
"react-markdown": "^9.0.1",
|
|
||||||
"react-slider": "^2.0.6",
|
"react-slider": "^2.0.6",
|
||||||
"react-sortablejs": "^6.1.4",
|
"react-sortablejs": "^6.1.4",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
|
|||||||
9
web/pnpm-lock.yaml
generated
9
web/pnpm-lock.yaml
generated
@ -133,6 +133,9 @@ importers:
|
|||||||
katex:
|
katex:
|
||||||
specifier: ^0.16.11
|
specifier: ^0.16.11
|
||||||
version: 0.16.11
|
version: 0.16.11
|
||||||
|
ky:
|
||||||
|
specifier: ^1.7.2
|
||||||
|
version: 1.7.2
|
||||||
lamejs:
|
lamejs:
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
@ -5612,6 +5615,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ky@1.7.2:
|
||||||
|
resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
lamejs@1.2.1:
|
lamejs@1.2.1:
|
||||||
resolution: {integrity: sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==}
|
resolution: {integrity: sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==}
|
||||||
|
|
||||||
@ -14744,6 +14751,8 @@ snapshots:
|
|||||||
|
|
||||||
kleur@4.1.5: {}
|
kleur@4.1.5: {}
|
||||||
|
|
||||||
|
ky@1.7.2: {}
|
||||||
|
|
||||||
lamejs@1.2.1:
|
lamejs@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
use-strict: 1.0.1
|
use-strict: 1.0.1
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { API_PREFIX, IS_CE_EDITION, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config'
|
import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
|
||||||
import { refreshAccessTokenOrRelogin } from './refresh-token'
|
import { refreshAccessTokenOrRelogin } from './refresh-token'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
|
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
|
||||||
@ -17,27 +17,10 @@ import type {
|
|||||||
WorkflowStartedResponse,
|
WorkflowStartedResponse,
|
||||||
} from '@/types/workflow'
|
} from '@/types/workflow'
|
||||||
import { removeAccessToken } from '@/app/components/share/utils'
|
import { removeAccessToken } from '@/app/components/share/utils'
|
||||||
|
import type { FetchOptionType, ResponseError } from './fetch'
|
||||||
|
import { ContentType, base, baseOptions, getPublicToken } from './fetch'
|
||||||
const TIME_OUT = 100000
|
const TIME_OUT = 100000
|
||||||
|
|
||||||
const ContentType = {
|
|
||||||
json: 'application/json',
|
|
||||||
stream: 'text/event-stream',
|
|
||||||
audio: 'audio/mpeg',
|
|
||||||
form: 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
||||||
download: 'application/octet-stream', // for download
|
|
||||||
upload: 'multipart/form-data', // for upload
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseOptions = {
|
|
||||||
method: 'GET',
|
|
||||||
mode: 'cors',
|
|
||||||
credentials: 'include', // always send cookies、HTTP Basic authentication.
|
|
||||||
headers: new Headers({
|
|
||||||
'Content-Type': ContentType.json,
|
|
||||||
}),
|
|
||||||
redirect: 'follow',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IOnDataMoreInfo = {
|
export type IOnDataMoreInfo = {
|
||||||
conversationId?: string
|
conversationId?: string
|
||||||
taskId?: string
|
taskId?: string
|
||||||
@ -100,17 +83,6 @@ export type IOtherOptions = {
|
|||||||
onTextReplace?: IOnTextReplace
|
onTextReplace?: IOnTextReplace
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseError = {
|
|
||||||
code: string
|
|
||||||
message: string
|
|
||||||
status: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type FetchOptionType = Omit<RequestInit, 'body'> & {
|
|
||||||
params?: Record<string, any>
|
|
||||||
body?: BodyInit | Record<string, any> | null
|
|
||||||
}
|
|
||||||
|
|
||||||
function unicodeToChar(text: string) {
|
function unicodeToChar(text: string) {
|
||||||
if (!text)
|
if (!text)
|
||||||
return ''
|
return ''
|
||||||
@ -277,153 +249,13 @@ const handleStream = (
|
|||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseFetch = <T>(
|
const baseFetch = base
|
||||||
url: string,
|
|
||||||
fetchOptions: FetchOptionType,
|
|
||||||
{
|
|
||||||
isPublicAPI = false,
|
|
||||||
isMarketplaceAPI = false,
|
|
||||||
bodyStringify = true,
|
|
||||||
needAllResponseContent,
|
|
||||||
deleteContentType,
|
|
||||||
getAbortController,
|
|
||||||
silent,
|
|
||||||
}: IOtherOptions,
|
|
||||||
): Promise<T> => {
|
|
||||||
const options: typeof baseOptions & FetchOptionType = Object.assign({}, baseOptions, fetchOptions)
|
|
||||||
if (isMarketplaceAPI)
|
|
||||||
options.credentials = 'omit'
|
|
||||||
|
|
||||||
if (getAbortController) {
|
|
||||||
const abortController = new AbortController()
|
|
||||||
getAbortController(abortController)
|
|
||||||
options.signal = abortController.signal
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPublicAPI) {
|
|
||||||
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
|
|
||||||
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
|
|
||||||
let accessTokenJson = { [sharedToken]: '' }
|
|
||||||
try {
|
|
||||||
accessTokenJson = JSON.parse(accessToken)
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
options.headers.set('Authorization', `Bearer ${accessTokenJson[sharedToken]}`)
|
|
||||||
}
|
|
||||||
else if (!isMarketplaceAPI) {
|
|
||||||
const accessToken = localStorage.getItem('console_token') || ''
|
|
||||||
options.headers.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deleteContentType) {
|
|
||||||
options.headers.delete('Content-Type')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const contentType = options.headers.get('Content-Type')
|
|
||||||
if (!contentType)
|
|
||||||
options.headers.set('Content-Type', ContentType.json)
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlPrefix = (() => {
|
|
||||||
if (isMarketplaceAPI)
|
|
||||||
return MARKETPLACE_API_PREFIX
|
|
||||||
if (isPublicAPI)
|
|
||||||
return PUBLIC_API_PREFIX
|
|
||||||
return API_PREFIX
|
|
||||||
})()
|
|
||||||
let urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
|
|
||||||
|
|
||||||
const { method, params, body } = options
|
|
||||||
// handle query
|
|
||||||
if (method === 'GET' && params) {
|
|
||||||
const paramsArray: string[] = []
|
|
||||||
Object.keys(params).forEach(key =>
|
|
||||||
paramsArray.push(`${key}=${encodeURIComponent(params[key])}`),
|
|
||||||
)
|
|
||||||
if (urlWithPrefix.search(/\?/) === -1)
|
|
||||||
urlWithPrefix += `?${paramsArray.join('&')}`
|
|
||||||
|
|
||||||
else
|
|
||||||
urlWithPrefix += `&${paramsArray.join('&')}`
|
|
||||||
|
|
||||||
delete options.params
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body && bodyStringify)
|
|
||||||
options.body = JSON.stringify(body)
|
|
||||||
|
|
||||||
// Handle timeout
|
|
||||||
return Promise.race([
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
reject(new Error('request timeout'))
|
|
||||||
}, TIME_OUT)
|
|
||||||
}),
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
globalThis.fetch(urlWithPrefix, options as RequestInit)
|
|
||||||
.then((res) => {
|
|
||||||
const resClone = res.clone()
|
|
||||||
// Error handler
|
|
||||||
if (!/^(2|3)\d{2}$/.test(String(res.status))) {
|
|
||||||
const bodyJson = res.json()
|
|
||||||
switch (res.status) {
|
|
||||||
case 401:
|
|
||||||
return Promise.reject(resClone)
|
|
||||||
case 403:
|
|
||||||
bodyJson.then((data: ResponseError) => {
|
|
||||||
if (!silent)
|
|
||||||
Toast.notify({ type: 'error', message: data.message })
|
|
||||||
if (data.code === 'already_setup')
|
|
||||||
globalThis.location.href = `${globalThis.location.origin}/signin`
|
|
||||||
})
|
|
||||||
break
|
|
||||||
// fall through
|
|
||||||
default:
|
|
||||||
bodyJson.then((data: ResponseError) => {
|
|
||||||
if (!silent)
|
|
||||||
Toast.notify({ type: 'error', message: data.message })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return Promise.reject(resClone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle delete api. Delete api not return content.
|
|
||||||
if (res.status === 204) {
|
|
||||||
resolve({ result: 'success' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// return data
|
|
||||||
if (options.headers.get('Content-type') === ContentType.download || options.headers.get('Content-type') === ContentType.audio)
|
|
||||||
resolve(needAllResponseContent ? resClone : res.blob())
|
|
||||||
|
|
||||||
else resolve(needAllResponseContent ? resClone : res.json())
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (!silent)
|
|
||||||
Toast.notify({ type: 'error', message: err })
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]) as Promise<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const upload = (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => {
|
export const upload = (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => {
|
||||||
const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
|
const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
|
||||||
let token = ''
|
let token = ''
|
||||||
if (isPublicAPI) {
|
if (isPublicAPI) {
|
||||||
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
|
token = getPublicToken()
|
||||||
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
|
|
||||||
let accessTokenJson = { [sharedToken]: '' }
|
|
||||||
try {
|
|
||||||
accessTokenJson = JSON.parse(accessToken)
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
token = accessTokenJson[sharedToken]
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const accessToken = localStorage.getItem('console_token') || ''
|
const accessToken = localStorage.getItem('console_token') || ''
|
||||||
@ -499,9 +331,9 @@ export const ssePost = (
|
|||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
}, fetchOptions)
|
}, fetchOptions)
|
||||||
|
|
||||||
const contentType = options.headers.get('Content-Type')
|
const contentType = (options.headers as Headers).get('Content-Type')
|
||||||
if (!contentType)
|
if (!contentType)
|
||||||
options.headers.set('Content-Type', ContentType.json)
|
(options.headers as Headers).set('Content-Type', ContentType.json)
|
||||||
|
|
||||||
getAbortController?.(abortController)
|
getAbortController?.(abortController)
|
||||||
|
|
||||||
@ -559,18 +391,17 @@ export const ssePost = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// base request
|
// base request
|
||||||
export const request = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
|
export const request = <T>(url: string, options = {}, otherOptions: IOtherOptions = {}) => {
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
const otherOptionsForBaseFetch = otherOptions || {}
|
baseFetch<T>(url, options, otherOptions).then(resolve).catch((errResp) => {
|
||||||
baseFetch<T>(url, options, otherOptionsForBaseFetch).then(resolve).catch((errResp) => {
|
|
||||||
if (errResp?.status === 401) {
|
if (errResp?.status === 401) {
|
||||||
return refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
|
return refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
|
||||||
baseFetch<T>(url, options, otherOptionsForBaseFetch).then(resolve).catch(reject)
|
baseFetch<T>(url, options, otherOptions).then(resolve).catch(reject)
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
const {
|
const {
|
||||||
isPublicAPI = false,
|
isPublicAPI = false,
|
||||||
silent,
|
silent,
|
||||||
} = otherOptionsForBaseFetch
|
} = otherOptions
|
||||||
const bodyJson = errResp.json()
|
const bodyJson = errResp.json()
|
||||||
if (isPublicAPI) {
|
if (isPublicAPI) {
|
||||||
return bodyJson.then((data: ResponseError) => {
|
return bodyJson.then((data: ResponseError) => {
|
||||||
|
|||||||
187
web/service/fetch.ts
Normal file
187
web/service/fetch.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import type { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, Hooks } from 'ky'
|
||||||
|
import ky from 'ky'
|
||||||
|
import type { IOtherOptions } from './base'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
import { API_PREFIX, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config'
|
||||||
|
|
||||||
|
const TIME_OUT = 100000
|
||||||
|
|
||||||
|
export const ContentType = {
|
||||||
|
json: 'application/json',
|
||||||
|
stream: 'text/event-stream',
|
||||||
|
audio: 'audio/mpeg',
|
||||||
|
form: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
download: 'application/octet-stream', // for download
|
||||||
|
upload: 'multipart/form-data', // for upload
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FetchOptionType = Omit<RequestInit, 'body'> & {
|
||||||
|
params?: Record<string, any>
|
||||||
|
body?: BodyInit | Record<string, any> | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterResponse204: AfterResponseHook = async (_request, _options, response) => {
|
||||||
|
if (response.status === 204) return Response.json({ result: 'success' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResponseError = {
|
||||||
|
code: string
|
||||||
|
message: string
|
||||||
|
status: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook => {
|
||||||
|
return async (_request, _options, response) => {
|
||||||
|
if (!/^(2|3)\d{2}$/.test(String(response.status))) {
|
||||||
|
const bodyJson = response.json() as Promise<ResponseError>
|
||||||
|
switch (response.status) {
|
||||||
|
case 401:
|
||||||
|
return Promise.reject(response)
|
||||||
|
case 403:
|
||||||
|
bodyJson.then((data: ResponseError) => {
|
||||||
|
if (!otherOptions.silent)
|
||||||
|
Toast.notify({ type: 'error', message: data.message })
|
||||||
|
if (data.code === 'already_setup')
|
||||||
|
globalThis.location.href = `${globalThis.location.origin}/signin`
|
||||||
|
})
|
||||||
|
break
|
||||||
|
// fall through
|
||||||
|
default:
|
||||||
|
bodyJson.then((data: ResponseError) => {
|
||||||
|
if (!otherOptions.silent)
|
||||||
|
Toast.notify({ type: 'error', message: data.message })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
throw response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeErrorToast = (otherOptions: IOtherOptions): BeforeErrorHook => {
|
||||||
|
return (error) => {
|
||||||
|
if (!otherOptions.silent)
|
||||||
|
Toast.notify({ type: 'error', message: error.message })
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPublicToken = () => {
|
||||||
|
let token = ''
|
||||||
|
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
|
||||||
|
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
|
||||||
|
let accessTokenJson = { [sharedToken]: '' }
|
||||||
|
try {
|
||||||
|
accessTokenJson = JSON.parse(accessToken)
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
token = accessTokenJson[sharedToken]
|
||||||
|
return token || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeRequestPublicAuthorization: BeforeRequestHook = (request) => {
|
||||||
|
const token = getPublicToken()
|
||||||
|
request.headers.set('Authorization', `Bearer ${token}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeRequestAuthorization: BeforeRequestHook = (request) => {
|
||||||
|
const accessToken = localStorage.getItem('console_token') || ''
|
||||||
|
request.headers.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeRequestDeleteContentType: BeforeRequestHook = (request) => {
|
||||||
|
request.headers.delete('Content-Type')
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseHooks: Hooks = {
|
||||||
|
afterResponse: [
|
||||||
|
afterResponse204,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = ky.create({
|
||||||
|
hooks: baseHooks,
|
||||||
|
timeout: TIME_OUT,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const baseOptions: RequestInit = {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'cors',
|
||||||
|
credentials: 'include', // always send cookies、HTTP Basic authentication.
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': ContentType.json,
|
||||||
|
}),
|
||||||
|
redirect: 'follow',
|
||||||
|
}
|
||||||
|
|
||||||
|
async function base<T>(url: string, options: FetchOptionType = {}, otherOptions: IOtherOptions = {}): Promise<T> {
|
||||||
|
const { params, body, ...init } = Object.assign({}, baseOptions, options)
|
||||||
|
const {
|
||||||
|
isPublicAPI = false,
|
||||||
|
isMarketplaceAPI = false,
|
||||||
|
bodyStringify = true,
|
||||||
|
needAllResponseContent,
|
||||||
|
deleteContentType,
|
||||||
|
getAbortController,
|
||||||
|
} = otherOptions
|
||||||
|
|
||||||
|
const base
|
||||||
|
= isMarketplaceAPI
|
||||||
|
? MARKETPLACE_API_PREFIX
|
||||||
|
: isPublicAPI
|
||||||
|
? PUBLIC_API_PREFIX
|
||||||
|
: API_PREFIX
|
||||||
|
|
||||||
|
if (getAbortController) {
|
||||||
|
const abortController = new AbortController()
|
||||||
|
getAbortController(abortController)
|
||||||
|
options.signal = abortController.signal
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPathname = `${base}${url.startsWith('/') ? url : `/${url}`}`
|
||||||
|
|
||||||
|
const res = await client.extend({
|
||||||
|
hooks: {
|
||||||
|
...baseHooks,
|
||||||
|
beforeError: [
|
||||||
|
...baseHooks.beforeError || [],
|
||||||
|
beforeErrorToast(otherOptions),
|
||||||
|
],
|
||||||
|
beforeRequest: [
|
||||||
|
...baseHooks.beforeRequest || [],
|
||||||
|
isPublicAPI && beforeRequestPublicAuthorization,
|
||||||
|
!isPublicAPI && !isMarketplaceAPI && beforeRequestAuthorization,
|
||||||
|
deleteContentType && beforeRequestDeleteContentType,
|
||||||
|
].filter(i => !!i),
|
||||||
|
afterResponse: [
|
||||||
|
...baseHooks.afterResponse || [],
|
||||||
|
afterResponseErrorCode(otherOptions),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})(fetchPathname, {
|
||||||
|
credentials: isMarketplaceAPI
|
||||||
|
? 'omit'
|
||||||
|
: (options.credentials || 'include'),
|
||||||
|
...init,
|
||||||
|
retry: {
|
||||||
|
methods: [],
|
||||||
|
},
|
||||||
|
...(bodyStringify ? { json: body } : { body: body as BodyInit }),
|
||||||
|
searchParams: params,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (needAllResponseContent)
|
||||||
|
return res as T
|
||||||
|
const contentType = res.headers.get('content-type')
|
||||||
|
if (
|
||||||
|
contentType
|
||||||
|
&& [ContentType.download, ContentType.audio].includes(contentType)
|
||||||
|
)
|
||||||
|
return await res.blob() as T
|
||||||
|
|
||||||
|
return await res.json() as T
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
client,
|
||||||
|
base,
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user