| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 | 
							- import { refreshAccessTokenOrRelogin } from './refresh-token'
 
- import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
 
- import Toast from '@/app/components/base/toast'
 
- import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
 
- import type { VisionFile } from '@/types/app'
 
- import type {
 
-   IterationFinishedResponse,
 
-   IterationNextResponse,
 
-   IterationStartedResponse,
 
-   NodeFinishedResponse,
 
-   NodeStartedResponse,
 
-   ParallelBranchFinishedResponse,
 
-   ParallelBranchStartedResponse,
 
-   TextChunkResponse,
 
-   TextReplaceResponse,
 
-   WorkflowFinishedResponse,
 
-   WorkflowStartedResponse,
 
- } from '@/types/workflow'
 
- import { removeAccessToken } from '@/app/components/share/utils'
 
- import { asyncRunSafe } from '@/utils'
 
- 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 = {
 
-   conversationId?: string
 
-   taskId?: string
 
-   messageId: string
 
-   errorMessage?: string
 
-   errorCode?: string
 
- }
 
- export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
 
- export type IOnThought = (though: ThoughtItem) => void
 
- export type IOnFile = (file: VisionFile) => void
 
- export type IOnMessageEnd = (messageEnd: MessageEnd) => void
 
- export type IOnMessageReplace = (messageReplace: MessageReplace) => void
 
- export type IOnAnnotationReply = (messageReplace: AnnotationReply) => void
 
- export type IOnCompleted = (hasError?: boolean, errorMessage?: string) => void
 
- export type IOnError = (msg: string, code?: string) => void
 
- export type IOnWorkflowStarted = (workflowStarted: WorkflowStartedResponse) => void
 
- export type IOnWorkflowFinished = (workflowFinished: WorkflowFinishedResponse) => void
 
- export type IOnNodeStarted = (nodeStarted: NodeStartedResponse) => void
 
- export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void
 
- export type IOnIterationStarted = (workflowStarted: IterationStartedResponse) => void
 
- export type IOnIterationNext = (workflowStarted: IterationNextResponse) => void
 
- export type IOnIterationFinished = (workflowFinished: IterationFinishedResponse) => void
 
- export type IOnParallelBranchStarted = (parallelBranchStarted: ParallelBranchStartedResponse) => void
 
- export type IOnParallelBranchFinished = (parallelBranchFinished: ParallelBranchFinishedResponse) => void
 
- export type IOnTextChunk = (textChunk: TextChunkResponse) => void
 
- export type IOnTTSChunk = (messageId: string, audioStr: string, audioType?: string) => void
 
- export type IOnTTSEnd = (messageId: string, audioStr: string, audioType?: string) => void
 
- export type IOnTextReplace = (textReplace: TextReplaceResponse) => void
 
- export type IOtherOptions = {
 
-   isPublicAPI?: boolean
 
-   bodyStringify?: boolean
 
-   needAllResponseContent?: boolean
 
-   deleteContentType?: boolean
 
-   silent?: boolean
 
-   onData?: IOnData // for stream
 
-   onThought?: IOnThought
 
-   onFile?: IOnFile
 
-   onMessageEnd?: IOnMessageEnd
 
-   onMessageReplace?: IOnMessageReplace
 
-   onError?: IOnError
 
-   onCompleted?: IOnCompleted // for stream
 
-   getAbortController?: (abortController: AbortController) => void
 
-   onWorkflowStarted?: IOnWorkflowStarted
 
-   onWorkflowFinished?: IOnWorkflowFinished
 
-   onNodeStarted?: IOnNodeStarted
 
-   onNodeFinished?: IOnNodeFinished
 
-   onIterationStart?: IOnIterationStarted
 
-   onIterationNext?: IOnIterationNext
 
-   onIterationFinish?: IOnIterationFinished
 
-   onParallelBranchStarted?: IOnParallelBranchStarted
 
-   onParallelBranchFinished?: IOnParallelBranchFinished
 
-   onTextChunk?: IOnTextChunk
 
-   onTTSChunk?: IOnTTSChunk
 
-   onTTSEnd?: IOnTTSEnd
 
-   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) {
 
-   if (!text)
 
-     return ''
 
-   return text.replace(/\\u[0-9a-f]{4}/g, (_match, p1) => {
 
-     return String.fromCharCode(parseInt(p1, 16))
 
-   })
 
- }
 
- function requiredWebSSOLogin() {
 
-   globalThis.location.href = `/webapp-signin?redirect_url=${globalThis.location.pathname}`
 
- }
 
- export function format(text: string) {
 
-   let res = text.trim()
 
-   if (res.startsWith('\n'))
 
-     res = res.replace('\n', '')
 
-   return res.replaceAll('\n', '<br/>').replaceAll('```', '')
 
- }
 
- const handleStream = (
 
-   response: Response,
 
-   onData: IOnData,
 
-   onCompleted?: IOnCompleted,
 
-   onThought?: IOnThought,
 
-   onMessageEnd?: IOnMessageEnd,
 
-   onMessageReplace?: IOnMessageReplace,
 
-   onFile?: IOnFile,
 
-   onWorkflowStarted?: IOnWorkflowStarted,
 
-   onWorkflowFinished?: IOnWorkflowFinished,
 
-   onNodeStarted?: IOnNodeStarted,
 
-   onNodeFinished?: IOnNodeFinished,
 
-   onIterationStart?: IOnIterationStarted,
 
-   onIterationNext?: IOnIterationNext,
 
-   onIterationFinish?: IOnIterationFinished,
 
-   onParallelBranchStarted?: IOnParallelBranchStarted,
 
-   onParallelBranchFinished?: IOnParallelBranchFinished,
 
-   onTextChunk?: IOnTextChunk,
 
-   onTTSChunk?: IOnTTSChunk,
 
-   onTTSEnd?: IOnTTSEnd,
 
-   onTextReplace?: IOnTextReplace,
 
- ) => {
 
-   if (!response.ok)
 
-     throw new Error('Network response was not ok')
 
-   const reader = response.body?.getReader()
 
-   const decoder = new TextDecoder('utf-8')
 
-   let buffer = ''
 
-   let bufferObj: Record<string, any>
 
-   let isFirstMessage = true
 
-   function read() {
 
-     let hasError = false
 
-     reader?.read().then((result: any) => {
 
-       if (result.done) {
 
-         onCompleted && onCompleted()
 
-         return
 
-       }
 
-       buffer += decoder.decode(result.value, { stream: true })
 
-       const lines = buffer.split('\n')
 
-       try {
 
-         lines.forEach((message) => {
 
-           if (message.startsWith('data: ')) { // check if it starts with data:
 
-             try {
 
-               bufferObj = JSON.parse(message.substring(6)) as Record<string, any>// remove data: and parse as json
 
-             }
 
-             catch (e) {
 
-               // mute handle message cut off
 
-               onData('', isFirstMessage, {
 
-                 conversationId: bufferObj?.conversation_id,
 
-                 messageId: bufferObj?.message_id,
 
-               })
 
-               return
 
-             }
 
-             if (bufferObj.status === 400 || !bufferObj.event) {
 
-               onData('', false, {
 
-                 conversationId: undefined,
 
-                 messageId: '',
 
-                 errorMessage: bufferObj?.message,
 
-                 errorCode: bufferObj?.code,
 
-               })
 
-               hasError = true
 
-               onCompleted?.(true, bufferObj?.message)
 
-               return
 
-             }
 
-             if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
 
-               // can not use format here. Because message is splitted.
 
-               onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
 
-                 conversationId: bufferObj.conversation_id,
 
-                 taskId: bufferObj.task_id,
 
-                 messageId: bufferObj.id,
 
-               })
 
-               isFirstMessage = false
 
-             }
 
-             else if (bufferObj.event === 'agent_thought') {
 
-               onThought?.(bufferObj as ThoughtItem)
 
-             }
 
-             else if (bufferObj.event === 'message_file') {
 
-               onFile?.(bufferObj as VisionFile)
 
-             }
 
-             else if (bufferObj.event === 'message_end') {
 
-               onMessageEnd?.(bufferObj as MessageEnd)
 
-             }
 
-             else if (bufferObj.event === 'message_replace') {
 
-               onMessageReplace?.(bufferObj as MessageReplace)
 
-             }
 
-             else if (bufferObj.event === 'workflow_started') {
 
-               onWorkflowStarted?.(bufferObj as WorkflowStartedResponse)
 
-             }
 
-             else if (bufferObj.event === 'workflow_finished') {
 
-               onWorkflowFinished?.(bufferObj as WorkflowFinishedResponse)
 
-             }
 
-             else if (bufferObj.event === 'node_started') {
 
-               onNodeStarted?.(bufferObj as NodeStartedResponse)
 
-             }
 
-             else if (bufferObj.event === 'node_finished') {
 
-               onNodeFinished?.(bufferObj as NodeFinishedResponse)
 
-             }
 
-             else if (bufferObj.event === 'iteration_started') {
 
-               onIterationStart?.(bufferObj as IterationStartedResponse)
 
-             }
 
-             else if (bufferObj.event === 'iteration_next') {
 
-               onIterationNext?.(bufferObj as IterationNextResponse)
 
-             }
 
-             else if (bufferObj.event === 'iteration_completed') {
 
-               onIterationFinish?.(bufferObj as IterationFinishedResponse)
 
-             }
 
-             else if (bufferObj.event === 'parallel_branch_started') {
 
-               onParallelBranchStarted?.(bufferObj as ParallelBranchStartedResponse)
 
-             }
 
-             else if (bufferObj.event === 'parallel_branch_finished') {
 
-               onParallelBranchFinished?.(bufferObj as ParallelBranchFinishedResponse)
 
-             }
 
-             else if (bufferObj.event === 'text_chunk') {
 
-               onTextChunk?.(bufferObj as TextChunkResponse)
 
-             }
 
-             else if (bufferObj.event === 'text_replace') {
 
-               onTextReplace?.(bufferObj as TextReplaceResponse)
 
-             }
 
-             else if (bufferObj.event === 'tts_message') {
 
-               onTTSChunk?.(bufferObj.message_id, bufferObj.audio, bufferObj.audio_type)
 
-             }
 
-             else if (bufferObj.event === 'tts_message_end') {
 
-               onTTSEnd?.(bufferObj.message_id, bufferObj.audio)
 
-             }
 
-           }
 
-         })
 
-         buffer = lines[lines.length - 1]
 
-       }
 
-       catch (e) {
 
-         onData('', false, {
 
-           conversationId: undefined,
 
-           messageId: '',
 
-           errorMessage: `${e}`,
 
-         })
 
-         hasError = true
 
-         onCompleted?.(true, e as string)
 
-         return
 
-       }
 
-       if (!hasError)
 
-         read()
 
-     })
 
-   }
 
-   read()
 
- }
 
- const baseFetch = <T>(
 
-   url: string,
 
-   fetchOptions: FetchOptionType,
 
-   {
 
-     isPublicAPI = false,
 
-     bodyStringify = true,
 
-     needAllResponseContent,
 
-     deleteContentType,
 
-     getAbortController,
 
-     silent,
 
-   }: IOtherOptions,
 
- ): Promise<T> => {
 
-   const options: typeof baseOptions & FetchOptionType = Object.assign({}, baseOptions, fetchOptions)
 
-   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 {
 
-     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 = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
 
-   let urlWithPrefix = (url.startsWith('http://') || url.startsWith('https://'))
 
-     ? url
 
-     : `${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> => {
 
-   const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
 
-   let token = ''
 
-   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) {
 
-     }
 
-     token = accessTokenJson[sharedToken]
 
-   }
 
-   else {
 
-     const accessToken = localStorage.getItem('console_token') || ''
 
-     token = accessToken
 
-   }
 
-   const defaultOptions = {
 
-     method: 'POST',
 
-     url: (url ? `${urlPrefix}${url}` : `${urlPrefix}/files/upload`) + (searchParams || ''),
 
-     headers: {
 
-       Authorization: `Bearer ${token}`,
 
-     },
 
-     data: {},
 
-   }
 
-   options = {
 
-     ...defaultOptions,
 
-     ...options,
 
-     headers: { ...defaultOptions.headers, ...options.headers },
 
-   }
 
-   return new Promise((resolve, reject) => {
 
-     const xhr = options.xhr
 
-     xhr.open(options.method, options.url)
 
-     for (const key in options.headers)
 
-       xhr.setRequestHeader(key, options.headers[key])
 
-     xhr.withCredentials = true
 
-     xhr.responseType = 'json'
 
-     xhr.onreadystatechange = function () {
 
-       if (xhr.readyState === 4) {
 
-         if (xhr.status === 201)
 
-           resolve(xhr.response)
 
-         else
 
-           reject(xhr)
 
-       }
 
-     }
 
-     xhr.upload.onprogress = options.onprogress
 
-     xhr.send(options.data)
 
-   })
 
- }
 
- export const ssePost = (
 
-   url: string,
 
-   fetchOptions: FetchOptionType,
 
-   otherOptions: IOtherOptions,
 
- ) => {
 
-   const {
 
-     isPublicAPI = false,
 
-     onData,
 
-     onCompleted,
 
-     onThought,
 
-     onFile,
 
-     onMessageEnd,
 
-     onMessageReplace,
 
-     onWorkflowStarted,
 
-     onWorkflowFinished,
 
-     onNodeStarted,
 
-     onNodeFinished,
 
-     onIterationStart,
 
-     onIterationNext,
 
-     onIterationFinish,
 
-     onParallelBranchStarted,
 
-     onParallelBranchFinished,
 
-     onTextChunk,
 
-     onTTSChunk,
 
-     onTTSEnd,
 
-     onTextReplace,
 
-     onError,
 
-     getAbortController,
 
-   } = otherOptions
 
-   const abortController = new AbortController()
 
-   const options = Object.assign({}, baseOptions, {
 
-     method: 'POST',
 
-     signal: abortController.signal,
 
-   }, fetchOptions)
 
-   const contentType = options.headers.get('Content-Type')
 
-   if (!contentType)
 
-     options.headers.set('Content-Type', ContentType.json)
 
-   getAbortController?.(abortController)
 
-   const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
 
-   const urlWithPrefix = (url.startsWith('http://') || url.startsWith('https://'))
 
-     ? url
 
-     : `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
 
-   const { body } = options
 
-   if (body)
 
-     options.body = JSON.stringify(body)
 
-   globalThis.fetch(urlWithPrefix, options as RequestInit)
 
-     .then((res) => {
 
-       if (!/^(2|3)\d{2}$/.test(String(res.status))) {
 
-         if (res.status === 401) {
 
-           refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
 
-             ssePost(url, fetchOptions, otherOptions)
 
-           }).catch(() => {
 
-             res.json().then((data: any) => {
 
-               if (isPublicAPI) {
 
-                 if (data.code === 'web_sso_auth_required')
 
-                   requiredWebSSOLogin()
 
-                 if (data.code === 'unauthorized') {
 
-                   removeAccessToken()
 
-                   globalThis.location.reload()
 
-                 }
 
-               }
 
-             })
 
-           })
 
-         }
 
-         else {
 
-           res.json().then((data) => {
 
-             Toast.notify({ type: 'error', message: data.message || 'Server Error' })
 
-           })
 
-           onError?.('Server Error')
 
-         }
 
-         return
 
-       }
 
-       return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
 
-         if (moreInfo.errorMessage) {
 
-           onError?.(moreInfo.errorMessage, moreInfo.errorCode)
 
-           // TypeError: Cannot assign to read only property ... will happen in page leave, so it should be ignored.
 
-           if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.' && !moreInfo.errorMessage.includes('TypeError: Cannot assign to read only property'))
 
-             Toast.notify({ type: 'error', message: moreInfo.errorMessage })
 
-           return
 
-         }
 
-         onData?.(str, isFirstMessage, moreInfo)
 
-       }, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace)
 
-     }).catch((e) => {
 
-       if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property'))
 
-         Toast.notify({ type: 'error', message: e })
 
-       onError?.(e)
 
-     })
 
- }
 
- // base request
 
- export const request = async<T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   try {
 
-     const otherOptionsForBaseFetch = otherOptions || {}
 
-     const [err, resp] = await asyncRunSafe<T>(baseFetch(url, options, otherOptionsForBaseFetch))
 
-     if (err === null)
 
-       return resp
 
-     const errResp: Response = err as any
 
-     if (errResp.status === 401) {
 
-       const [parseErr, errRespData] = await asyncRunSafe<ResponseError>(errResp.json())
 
-       const loginUrl = `${globalThis.location.origin}/signin`
 
-       if (parseErr) {
 
-         globalThis.location.href = loginUrl
 
-         return Promise.reject(err)
 
-       }
 
-       // special code
 
-       const { code, message } = errRespData
 
-       // webapp sso
 
-       if (code === 'web_sso_auth_required') {
 
-         requiredWebSSOLogin()
 
-         return Promise.reject(err)
 
-       }
 
-       if (code === 'unauthorized_and_force_logout') {
 
-         localStorage.removeItem('console_token')
 
-         localStorage.removeItem('refresh_token')
 
-         globalThis.location.reload()
 
-         return Promise.reject(err)
 
-       }
 
-       const {
 
-         isPublicAPI = false,
 
-         silent,
 
-       } = otherOptionsForBaseFetch
 
-       if (isPublicAPI && code === 'unauthorized') {
 
-         removeAccessToken()
 
-         globalThis.location.reload()
 
-         return Promise.reject(err)
 
-       }
 
-       if (code === 'init_validate_failed' && IS_CE_EDITION && !silent) {
 
-         Toast.notify({ type: 'error', message, duration: 4000 })
 
-         return Promise.reject(err)
 
-       }
 
-       if (code === 'not_init_validated' && IS_CE_EDITION) {
 
-         globalThis.location.href = `${globalThis.location.origin}/init`
 
-         return Promise.reject(err)
 
-       }
 
-       if (code === 'not_setup' && IS_CE_EDITION) {
 
-         globalThis.location.href = `${globalThis.location.origin}/install`
 
-         return Promise.reject(err)
 
-       }
 
-       // refresh token
 
-       const [refreshErr] = await asyncRunSafe(refreshAccessTokenOrRelogin(TIME_OUT))
 
-       if (refreshErr === null)
 
-         return baseFetch<T>(url, options, otherOptionsForBaseFetch)
 
-       if (location.pathname !== '/signin' || !IS_CE_EDITION) {
 
-         globalThis.location.href = loginUrl
 
-         return Promise.reject(err)
 
-       }
 
-       if (!silent) {
 
-         Toast.notify({ type: 'error', message })
 
-         return Promise.reject(err)
 
-       }
 
-       globalThis.location.href = loginUrl
 
-       return Promise.reject(err)
 
-     }
 
-     else {
 
-       return Promise.reject(err)
 
-     }
 
-   }
 
-   catch (error) {
 
-     console.error(error)
 
-     return Promise.reject(error)
 
-   }
 
- }
 
- // request methods
 
- export const get = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return request<T>(url, Object.assign({}, options, { method: 'GET' }), otherOptions)
 
- }
 
- // For public API
 
- export const getPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return get<T>(url, options, { ...otherOptions, isPublicAPI: true })
 
- }
 
- export const post = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return request<T>(url, Object.assign({}, options, { method: 'POST' }), otherOptions)
 
- }
 
- export const postPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return post<T>(url, options, { ...otherOptions, isPublicAPI: true })
 
- }
 
- export const put = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return request<T>(url, Object.assign({}, options, { method: 'PUT' }), otherOptions)
 
- }
 
- export const putPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return put<T>(url, options, { ...otherOptions, isPublicAPI: true })
 
- }
 
- export const del = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return request<T>(url, Object.assign({}, options, { method: 'DELETE' }), otherOptions)
 
- }
 
- export const delPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return del<T>(url, options, { ...otherOptions, isPublicAPI: true })
 
- }
 
- export const patch = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return request<T>(url, Object.assign({}, options, { method: 'PATCH' }), otherOptions)
 
- }
 
- export const patchPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
 
-   return patch<T>(url, options, { ...otherOptions, isPublicAPI: true })
 
- }
 
 
  |