You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base.ts 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
  2. import { refreshAccessTokenOrRelogin } from './refresh-token'
  3. import Toast from '@/app/components/base/toast'
  4. import { basePath } from '@/utils/var'
  5. import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
  6. import type { VisionFile } from '@/types/app'
  7. import type {
  8. AgentLogResponse,
  9. IterationFinishedResponse,
  10. IterationNextResponse,
  11. IterationStartedResponse,
  12. LoopFinishedResponse,
  13. LoopNextResponse,
  14. LoopStartedResponse,
  15. NodeFinishedResponse,
  16. NodeStartedResponse,
  17. ParallelBranchFinishedResponse,
  18. ParallelBranchStartedResponse,
  19. TextChunkResponse,
  20. TextReplaceResponse,
  21. WorkflowFinishedResponse,
  22. WorkflowStartedResponse,
  23. } from '@/types/workflow'
  24. import { removeAccessToken } from '@/app/components/share/utils'
  25. import type { FetchOptionType, ResponseError } from './fetch'
  26. import { ContentType, base, baseOptions, getAccessToken } from './fetch'
  27. import { asyncRunSafe } from '@/utils'
  28. import type {
  29. DataSourceNodeCompletedResponse,
  30. DataSourceNodeErrorResponse,
  31. DataSourceNodeProcessingResponse,
  32. } from '@/types/pipeline'
  33. const TIME_OUT = 100000
  34. export type IOnDataMoreInfo = {
  35. conversationId?: string
  36. taskId?: string
  37. messageId: string
  38. errorMessage?: string
  39. errorCode?: string
  40. }
  41. export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
  42. export type IOnThought = (though: ThoughtItem) => void
  43. export type IOnFile = (file: VisionFile) => void
  44. export type IOnMessageEnd = (messageEnd: MessageEnd) => void
  45. export type IOnMessageReplace = (messageReplace: MessageReplace) => void
  46. export type IOnAnnotationReply = (messageReplace: AnnotationReply) => void
  47. export type IOnCompleted = (hasError?: boolean, errorMessage?: string) => void
  48. export type IOnError = (msg: string, code?: string) => void
  49. export type IOnWorkflowStarted = (workflowStarted: WorkflowStartedResponse) => void
  50. export type IOnWorkflowFinished = (workflowFinished: WorkflowFinishedResponse) => void
  51. export type IOnNodeStarted = (nodeStarted: NodeStartedResponse) => void
  52. export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void
  53. export type IOnIterationStarted = (workflowStarted: IterationStartedResponse) => void
  54. export type IOnIterationNext = (workflowStarted: IterationNextResponse) => void
  55. export type IOnNodeRetry = (nodeFinished: NodeFinishedResponse) => void
  56. export type IOnIterationFinished = (workflowFinished: IterationFinishedResponse) => void
  57. export type IOnParallelBranchStarted = (parallelBranchStarted: ParallelBranchStartedResponse) => void
  58. export type IOnParallelBranchFinished = (parallelBranchFinished: ParallelBranchFinishedResponse) => void
  59. export type IOnTextChunk = (textChunk: TextChunkResponse) => void
  60. export type IOnTTSChunk = (messageId: string, audioStr: string, audioType?: string) => void
  61. export type IOnTTSEnd = (messageId: string, audioStr: string, audioType?: string) => void
  62. export type IOnTextReplace = (textReplace: TextReplaceResponse) => void
  63. export type IOnLoopStarted = (workflowStarted: LoopStartedResponse) => void
  64. export type IOnLoopNext = (workflowStarted: LoopNextResponse) => void
  65. export type IOnLoopFinished = (workflowFinished: LoopFinishedResponse) => void
  66. export type IOnAgentLog = (agentLog: AgentLogResponse) => void
  67. export type IOnDataSourceNodeProcessing = (dataSourceNodeProcessing: DataSourceNodeProcessingResponse) => void
  68. export type IOnDataSourceNodeCompleted = (dataSourceNodeCompleted: DataSourceNodeCompletedResponse) => void
  69. export type IOnDataSourceNodeError = (dataSourceNodeError: DataSourceNodeErrorResponse) => void
  70. export type IOtherOptions = {
  71. isPublicAPI?: boolean
  72. isMarketplaceAPI?: boolean
  73. bodyStringify?: boolean
  74. needAllResponseContent?: boolean
  75. deleteContentType?: boolean
  76. silent?: boolean
  77. onData?: IOnData // for stream
  78. onThought?: IOnThought
  79. onFile?: IOnFile
  80. onMessageEnd?: IOnMessageEnd
  81. onMessageReplace?: IOnMessageReplace
  82. onError?: IOnError
  83. onCompleted?: IOnCompleted // for stream
  84. getAbortController?: (abortController: AbortController) => void
  85. onWorkflowStarted?: IOnWorkflowStarted
  86. onWorkflowFinished?: IOnWorkflowFinished
  87. onNodeStarted?: IOnNodeStarted
  88. onNodeFinished?: IOnNodeFinished
  89. onIterationStart?: IOnIterationStarted
  90. onIterationNext?: IOnIterationNext
  91. onIterationFinish?: IOnIterationFinished
  92. onNodeRetry?: IOnNodeRetry
  93. onParallelBranchStarted?: IOnParallelBranchStarted
  94. onParallelBranchFinished?: IOnParallelBranchFinished
  95. onTextChunk?: IOnTextChunk
  96. onTTSChunk?: IOnTTSChunk
  97. onTTSEnd?: IOnTTSEnd
  98. onTextReplace?: IOnTextReplace
  99. onLoopStart?: IOnLoopStarted
  100. onLoopNext?: IOnLoopNext
  101. onLoopFinish?: IOnLoopFinished
  102. onAgentLog?: IOnAgentLog
  103. // Pipeline data source node run
  104. onDataSourceNodeProcessing?: IOnDataSourceNodeProcessing
  105. onDataSourceNodeCompleted?: IOnDataSourceNodeCompleted
  106. onDataSourceNodeError?: IOnDataSourceNodeError
  107. }
  108. function unicodeToChar(text: string) {
  109. if (!text)
  110. return ''
  111. return text.replace(/\\u([0-9a-f]{4})/g, (_match, p1) => {
  112. return String.fromCharCode(Number.parseInt(p1, 16))
  113. })
  114. }
  115. function requiredWebSSOLogin(message?: string, code?: number) {
  116. const params = new URLSearchParams()
  117. params.append('redirect_url', encodeURIComponent(`${globalThis.location.pathname}${globalThis.location.search}`))
  118. if (message)
  119. params.append('message', message)
  120. if (code)
  121. params.append('code', String(code))
  122. globalThis.location.href = `${globalThis.location.origin}${basePath}/webapp-signin?${params.toString()}`
  123. }
  124. export function format(text: string) {
  125. let res = text.trim()
  126. if (res.startsWith('\n'))
  127. res = res.replace('\n', '')
  128. return res.replaceAll('\n', '<br/>').replaceAll('```', '')
  129. }
  130. const handleStream = (
  131. response: Response,
  132. onData: IOnData,
  133. onCompleted?: IOnCompleted,
  134. onThought?: IOnThought,
  135. onMessageEnd?: IOnMessageEnd,
  136. onMessageReplace?: IOnMessageReplace,
  137. onFile?: IOnFile,
  138. onWorkflowStarted?: IOnWorkflowStarted,
  139. onWorkflowFinished?: IOnWorkflowFinished,
  140. onNodeStarted?: IOnNodeStarted,
  141. onNodeFinished?: IOnNodeFinished,
  142. onIterationStart?: IOnIterationStarted,
  143. onIterationNext?: IOnIterationNext,
  144. onIterationFinish?: IOnIterationFinished,
  145. onLoopStart?: IOnLoopStarted,
  146. onLoopNext?: IOnLoopNext,
  147. onLoopFinish?: IOnLoopFinished,
  148. onNodeRetry?: IOnNodeRetry,
  149. onParallelBranchStarted?: IOnParallelBranchStarted,
  150. onParallelBranchFinished?: IOnParallelBranchFinished,
  151. onTextChunk?: IOnTextChunk,
  152. onTTSChunk?: IOnTTSChunk,
  153. onTTSEnd?: IOnTTSEnd,
  154. onTextReplace?: IOnTextReplace,
  155. onAgentLog?: IOnAgentLog,
  156. onDataSourceNodeProcessing?: IOnDataSourceNodeProcessing,
  157. onDataSourceNodeCompleted?: IOnDataSourceNodeCompleted,
  158. onDataSourceNodeError?: IOnDataSourceNodeError,
  159. ) => {
  160. if (!response.ok)
  161. throw new Error('Network response was not ok')
  162. const reader = response.body?.getReader()
  163. const decoder = new TextDecoder('utf-8')
  164. let buffer = ''
  165. let bufferObj: Record<string, any>
  166. let isFirstMessage = true
  167. function read() {
  168. let hasError = false
  169. reader?.read().then((result: any) => {
  170. if (result.done) {
  171. onCompleted && onCompleted()
  172. return
  173. }
  174. buffer += decoder.decode(result.value, { stream: true })
  175. const lines = buffer.split('\n')
  176. try {
  177. lines.forEach((message) => {
  178. if (message.startsWith('data: ')) { // check if it starts with data:
  179. try {
  180. bufferObj = JSON.parse(message.substring(6)) as Record<string, any>// remove data: and parse as json
  181. }
  182. catch {
  183. // mute handle message cut off
  184. onData('', isFirstMessage, {
  185. conversationId: bufferObj?.conversation_id,
  186. messageId: bufferObj?.message_id,
  187. })
  188. return
  189. }
  190. if (bufferObj.status === 400 || !bufferObj.event) {
  191. onData('', false, {
  192. conversationId: undefined,
  193. messageId: '',
  194. errorMessage: bufferObj?.message,
  195. errorCode: bufferObj?.code,
  196. })
  197. hasError = true
  198. onCompleted?.(true, bufferObj?.message)
  199. return
  200. }
  201. if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
  202. // can not use format here. Because message is splitted.
  203. onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
  204. conversationId: bufferObj.conversation_id,
  205. taskId: bufferObj.task_id,
  206. messageId: bufferObj.id,
  207. })
  208. isFirstMessage = false
  209. }
  210. else if (bufferObj.event === 'agent_thought') {
  211. onThought?.(bufferObj as ThoughtItem)
  212. }
  213. else if (bufferObj.event === 'message_file') {
  214. onFile?.(bufferObj as VisionFile)
  215. }
  216. else if (bufferObj.event === 'message_end') {
  217. onMessageEnd?.(bufferObj as MessageEnd)
  218. }
  219. else if (bufferObj.event === 'message_replace') {
  220. onMessageReplace?.(bufferObj as MessageReplace)
  221. }
  222. else if (bufferObj.event === 'workflow_started') {
  223. onWorkflowStarted?.(bufferObj as WorkflowStartedResponse)
  224. }
  225. else if (bufferObj.event === 'workflow_finished') {
  226. onWorkflowFinished?.(bufferObj as WorkflowFinishedResponse)
  227. }
  228. else if (bufferObj.event === 'node_started') {
  229. onNodeStarted?.(bufferObj as NodeStartedResponse)
  230. }
  231. else if (bufferObj.event === 'node_finished') {
  232. onNodeFinished?.(bufferObj as NodeFinishedResponse)
  233. }
  234. else if (bufferObj.event === 'iteration_started') {
  235. onIterationStart?.(bufferObj as IterationStartedResponse)
  236. }
  237. else if (bufferObj.event === 'iteration_next') {
  238. onIterationNext?.(bufferObj as IterationNextResponse)
  239. }
  240. else if (bufferObj.event === 'iteration_completed') {
  241. onIterationFinish?.(bufferObj as IterationFinishedResponse)
  242. }
  243. else if (bufferObj.event === 'loop_started') {
  244. onLoopStart?.(bufferObj as LoopStartedResponse)
  245. }
  246. else if (bufferObj.event === 'loop_next') {
  247. onLoopNext?.(bufferObj as LoopNextResponse)
  248. }
  249. else if (bufferObj.event === 'loop_completed') {
  250. onLoopFinish?.(bufferObj as LoopFinishedResponse)
  251. }
  252. else if (bufferObj.event === 'node_retry') {
  253. onNodeRetry?.(bufferObj as NodeFinishedResponse)
  254. }
  255. else if (bufferObj.event === 'parallel_branch_started') {
  256. onParallelBranchStarted?.(bufferObj as ParallelBranchStartedResponse)
  257. }
  258. else if (bufferObj.event === 'parallel_branch_finished') {
  259. onParallelBranchFinished?.(bufferObj as ParallelBranchFinishedResponse)
  260. }
  261. else if (bufferObj.event === 'text_chunk') {
  262. onTextChunk?.(bufferObj as TextChunkResponse)
  263. }
  264. else if (bufferObj.event === 'text_replace') {
  265. onTextReplace?.(bufferObj as TextReplaceResponse)
  266. }
  267. else if (bufferObj.event === 'agent_log') {
  268. onAgentLog?.(bufferObj as AgentLogResponse)
  269. }
  270. else if (bufferObj.event === 'tts_message') {
  271. onTTSChunk?.(bufferObj.message_id, bufferObj.audio, bufferObj.audio_type)
  272. }
  273. else if (bufferObj.event === 'tts_message_end') {
  274. onTTSEnd?.(bufferObj.message_id, bufferObj.audio)
  275. }
  276. else if (bufferObj.event === 'datasource_processing') {
  277. onDataSourceNodeProcessing?.(bufferObj as DataSourceNodeProcessingResponse)
  278. }
  279. else if (bufferObj.event === 'datasource_completed') {
  280. onDataSourceNodeCompleted?.(bufferObj as DataSourceNodeCompletedResponse)
  281. }
  282. else if (bufferObj.event === 'datasource_error') {
  283. onDataSourceNodeError?.(bufferObj as DataSourceNodeErrorResponse)
  284. }
  285. else {
  286. console.warn(`Unknown event: ${bufferObj.event}`, bufferObj)
  287. }
  288. }
  289. })
  290. buffer = lines[lines.length - 1]
  291. }
  292. catch (e) {
  293. onData('', false, {
  294. conversationId: undefined,
  295. messageId: '',
  296. errorMessage: `${e}`,
  297. })
  298. hasError = true
  299. onCompleted?.(true, e as string)
  300. return
  301. }
  302. if (!hasError)
  303. read()
  304. })
  305. }
  306. read()
  307. }
  308. const baseFetch = base
  309. export const upload = async (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => {
  310. const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
  311. const token = await getAccessToken(isPublicAPI)
  312. const defaultOptions = {
  313. method: 'POST',
  314. url: (url ? `${urlPrefix}${url}` : `${urlPrefix}/files/upload`) + (searchParams || ''),
  315. headers: {
  316. Authorization: `Bearer ${token}`,
  317. },
  318. data: {},
  319. }
  320. options = {
  321. ...defaultOptions,
  322. ...options,
  323. headers: { ...defaultOptions.headers, ...options.headers },
  324. }
  325. return new Promise((resolve, reject) => {
  326. const xhr = options.xhr
  327. xhr.open(options.method, options.url)
  328. for (const key in options.headers)
  329. xhr.setRequestHeader(key, options.headers[key])
  330. xhr.withCredentials = true
  331. xhr.responseType = 'json'
  332. xhr.onreadystatechange = function () {
  333. if (xhr.readyState === 4) {
  334. if (xhr.status === 201)
  335. resolve(xhr.response)
  336. else
  337. reject(xhr)
  338. }
  339. }
  340. xhr.upload.onprogress = options.onprogress
  341. xhr.send(options.data)
  342. })
  343. }
  344. export const ssePost = async (
  345. url: string,
  346. fetchOptions: FetchOptionType,
  347. otherOptions: IOtherOptions,
  348. ) => {
  349. const {
  350. isPublicAPI = false,
  351. onData,
  352. onCompleted,
  353. onThought,
  354. onFile,
  355. onMessageEnd,
  356. onMessageReplace,
  357. onWorkflowStarted,
  358. onWorkflowFinished,
  359. onNodeStarted,
  360. onNodeFinished,
  361. onIterationStart,
  362. onIterationNext,
  363. onIterationFinish,
  364. onNodeRetry,
  365. onParallelBranchStarted,
  366. onParallelBranchFinished,
  367. onTextChunk,
  368. onTTSChunk,
  369. onTTSEnd,
  370. onTextReplace,
  371. onAgentLog,
  372. onError,
  373. getAbortController,
  374. onLoopStart,
  375. onLoopNext,
  376. onLoopFinish,
  377. onDataSourceNodeProcessing,
  378. onDataSourceNodeCompleted,
  379. onDataSourceNodeError,
  380. } = otherOptions
  381. const abortController = new AbortController()
  382. const token = localStorage.getItem('console_token')
  383. const options = Object.assign({}, baseOptions, {
  384. method: 'POST',
  385. signal: abortController.signal,
  386. headers: new Headers({
  387. Authorization: `Bearer ${token}`,
  388. }),
  389. } as RequestInit, fetchOptions)
  390. const contentType = (options.headers as Headers).get('Content-Type')
  391. if (!contentType)
  392. (options.headers as Headers).set('Content-Type', ContentType.json)
  393. getAbortController?.(abortController)
  394. const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
  395. const urlWithPrefix = (url.startsWith('http://') || url.startsWith('https://'))
  396. ? url
  397. : `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
  398. const { body } = options
  399. if (body)
  400. options.body = JSON.stringify(body)
  401. const accessToken = await getAccessToken(isPublicAPI)
  402. ; (options.headers as Headers).set('Authorization', `Bearer ${accessToken}`)
  403. globalThis.fetch(urlWithPrefix, options as RequestInit)
  404. .then((res) => {
  405. if (!/^[23]\d{2}$/.test(String(res.status))) {
  406. if (res.status === 401) {
  407. if (isPublicAPI) {
  408. res.json().then((data: any) => {
  409. if (isPublicAPI) {
  410. if (data.code === 'web_app_access_denied')
  411. requiredWebSSOLogin(data.message, 403)
  412. if (data.code === 'web_sso_auth_required') {
  413. removeAccessToken()
  414. requiredWebSSOLogin()
  415. }
  416. if (data.code === 'unauthorized') {
  417. removeAccessToken()
  418. requiredWebSSOLogin()
  419. }
  420. }
  421. })
  422. }
  423. else {
  424. refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
  425. ssePost(url, fetchOptions, otherOptions)
  426. }).catch((err) => {
  427. console.error(err)
  428. })
  429. }
  430. }
  431. else {
  432. res.json().then((data) => {
  433. Toast.notify({ type: 'error', message: data.message || 'Server Error' })
  434. })
  435. onError?.('Server Error')
  436. }
  437. return
  438. }
  439. return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
  440. if (moreInfo.errorMessage) {
  441. onError?.(moreInfo.errorMessage, moreInfo.errorCode)
  442. // TypeError: Cannot assign to read only property ... will happen in page leave, so it should be ignored.
  443. if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.' && !moreInfo.errorMessage.includes('TypeError: Cannot assign to read only property'))
  444. Toast.notify({ type: 'error', message: moreInfo.errorMessage })
  445. return
  446. }
  447. onData?.(str, isFirstMessage, moreInfo)
  448. },
  449. onCompleted,
  450. onThought,
  451. onMessageEnd,
  452. onMessageReplace,
  453. onFile,
  454. onWorkflowStarted,
  455. onWorkflowFinished,
  456. onNodeStarted,
  457. onNodeFinished,
  458. onIterationStart,
  459. onIterationNext,
  460. onIterationFinish,
  461. onLoopStart,
  462. onLoopNext,
  463. onLoopFinish,
  464. onNodeRetry,
  465. onParallelBranchStarted,
  466. onParallelBranchFinished,
  467. onTextChunk,
  468. onTTSChunk,
  469. onTTSEnd,
  470. onTextReplace,
  471. onAgentLog,
  472. onDataSourceNodeProcessing,
  473. onDataSourceNodeCompleted,
  474. onDataSourceNodeError,
  475. )
  476. }).catch((e) => {
  477. if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property'))
  478. Toast.notify({ type: 'error', message: e })
  479. onError?.(e)
  480. })
  481. }
  482. // base request
  483. export const request = async<T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  484. try {
  485. const otherOptionsForBaseFetch = otherOptions || {}
  486. const [err, resp] = await asyncRunSafe<T>(baseFetch(url, options, otherOptionsForBaseFetch))
  487. if (err === null)
  488. return resp
  489. const errResp: Response = err as any
  490. if (errResp.status === 401) {
  491. const [parseErr, errRespData] = await asyncRunSafe<ResponseError>(errResp.json())
  492. const loginUrl = `${globalThis.location.origin}${basePath}/signin`
  493. if (parseErr) {
  494. globalThis.location.href = loginUrl
  495. return Promise.reject(err)
  496. }
  497. // special code
  498. const { code, message } = errRespData
  499. // webapp sso
  500. if (code === 'web_app_access_denied') {
  501. requiredWebSSOLogin(message, 403)
  502. return Promise.reject(err)
  503. }
  504. if (code === 'web_sso_auth_required') {
  505. removeAccessToken()
  506. requiredWebSSOLogin()
  507. return Promise.reject(err)
  508. }
  509. if (code === 'unauthorized_and_force_logout') {
  510. localStorage.removeItem('console_token')
  511. localStorage.removeItem('refresh_token')
  512. globalThis.location.reload()
  513. return Promise.reject(err)
  514. }
  515. const {
  516. isPublicAPI = false,
  517. silent,
  518. } = otherOptionsForBaseFetch
  519. if (isPublicAPI && code === 'unauthorized') {
  520. removeAccessToken()
  521. requiredWebSSOLogin()
  522. return Promise.reject(err)
  523. }
  524. if (code === 'init_validate_failed' && IS_CE_EDITION && !silent) {
  525. Toast.notify({ type: 'error', message, duration: 4000 })
  526. return Promise.reject(err)
  527. }
  528. if (code === 'not_init_validated' && IS_CE_EDITION) {
  529. globalThis.location.href = `${globalThis.location.origin}${basePath}/init`
  530. return Promise.reject(err)
  531. }
  532. if (code === 'not_setup' && IS_CE_EDITION) {
  533. globalThis.location.href = `${globalThis.location.origin}${basePath}/install`
  534. return Promise.reject(err)
  535. }
  536. // refresh token
  537. const [refreshErr] = await asyncRunSafe(refreshAccessTokenOrRelogin(TIME_OUT))
  538. if (refreshErr === null)
  539. return baseFetch<T>(url, options, otherOptionsForBaseFetch)
  540. if (location.pathname !== `${basePath}/signin` || !IS_CE_EDITION) {
  541. globalThis.location.href = loginUrl
  542. return Promise.reject(err)
  543. }
  544. if (!silent) {
  545. Toast.notify({ type: 'error', message })
  546. return Promise.reject(err)
  547. }
  548. globalThis.location.href = loginUrl
  549. return Promise.reject(err)
  550. }
  551. else {
  552. return Promise.reject(err)
  553. }
  554. }
  555. catch (error) {
  556. console.error(error)
  557. return Promise.reject(error)
  558. }
  559. }
  560. // request methods
  561. export const get = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  562. return request<T>(url, Object.assign({}, options, { method: 'GET' }), otherOptions)
  563. }
  564. // For public API
  565. export const getPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  566. return get<T>(url, options, { ...otherOptions, isPublicAPI: true })
  567. }
  568. // For Marketplace API
  569. export const getMarketplace = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  570. return get<T>(url, options, { ...otherOptions, isMarketplaceAPI: true })
  571. }
  572. export const post = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  573. return request<T>(url, Object.assign({}, options, { method: 'POST' }), otherOptions)
  574. }
  575. // For Marketplace API
  576. export const postMarketplace = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  577. return post<T>(url, options, { ...otherOptions, isMarketplaceAPI: true })
  578. }
  579. export const postPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  580. return post<T>(url, options, { ...otherOptions, isPublicAPI: true })
  581. }
  582. export const put = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  583. return request<T>(url, Object.assign({}, options, { method: 'PUT' }), otherOptions)
  584. }
  585. export const putPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  586. return put<T>(url, options, { ...otherOptions, isPublicAPI: true })
  587. }
  588. export const del = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  589. return request<T>(url, Object.assign({}, options, { method: 'DELETE' }), otherOptions)
  590. }
  591. export const delPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  592. return del<T>(url, options, { ...otherOptions, isPublicAPI: true })
  593. }
  594. export const patch = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  595. return request<T>(url, Object.assign({}, options, { method: 'PATCH' }), otherOptions)
  596. }
  597. export const patchPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
  598. return patch<T>(url, options, { ...otherOptions, isPublicAPI: true })
  599. }