From 079620714eca76101a97f87bd8330154d76f2349 Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Fri, 19 Dec 2025 17:34:14 +0800
Subject: [PATCH] refactor: migrate common service toward TanStack Query
(#29009)
---
web/app/activate/activateForm.tsx | 32 +-
.../settings-modal/index.spec.tsx | 504 ++++++++++--------
.../dataset-config/settings-modal/index.tsx | 19 +-
.../components/app/configuration/index.tsx | 5 +-
.../tools/external-data-tool-modal.tsx | 8 +-
.../new-feature-panel/moderation/index.tsx | 8 +-
.../moderation/moderation-setting-modal.tsx | 15 +-
.../datasets/settings/form/index.tsx | 23 +-
web/app/components/explore/index.tsx | 16 +-
.../Integrations-page/index.tsx | 48 +-
.../api-based-extension-page/index.tsx | 8 +-
.../api-based-extension-page/selector.tsx | 8 +-
.../data-source-notion/index.tsx | 15 +-
.../data-source-notion/operate/index.tsx | 6 +-
.../account-setting/members-page/index.tsx | 15 +-
.../member-selector.tsx | 11 +-
.../model-provider-page/hooks.spec.ts | 21 +-
.../model-provider-page/hooks.ts | 78 +--
.../model-parameter-modal/index.tsx | 5 +-
.../provider-added-card/model-list-item.tsx | 9 +-
.../provider-added-card/model-list.tsx | 1 +
.../account-setting/plugin-page/index.tsx | 5 +-
.../model-selector/llm-params-panel.tsx | 9 +-
.../_base/components/file-upload-setting.tsx | 5 +-
.../forgot-password/ChangePasswordForm.tsx | 20 +-
web/app/signin/invite-settings/page.tsx | 8 +-
web/app/signin/one-more-step.tsx | 43 +-
web/context/app-context.tsx | 74 +--
web/context/provider-context.tsx | 24 +-
web/context/workspace-context.tsx | 5 +-
web/hooks/use-pay.tsx | 12 +-
web/service/common.ts | 207 ++++---
web/service/use-common.ts | 251 ++++++++-
33 files changed, 885 insertions(+), 633 deletions(-)
diff --git a/web/app/activate/activateForm.tsx b/web/app/activate/activateForm.tsx
index 4789a579a7..11fc4866f3 100644
--- a/web/app/activate/activateForm.tsx
+++ b/web/app/activate/activateForm.tsx
@@ -1,13 +1,13 @@
'use client'
+import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import { useRouter, useSearchParams } from 'next/navigation'
import { cn } from '@/utils/classnames'
import Button from '@/app/components/base/button'
-import { invitationCheck } from '@/service/common'
import Loading from '@/app/components/base/loading'
import useDocumentTitle from '@/hooks/use-document-title'
+import { useInvitationCheck } from '@/service/use-common'
const ActivateForm = () => {
useDocumentTitle('')
@@ -26,19 +26,21 @@ const ActivateForm = () => {
token,
},
}
- const { data: checkRes } = useSWR(checkParams, invitationCheck, {
- revalidateOnFocus: false,
- onSuccess(data) {
- if (data.is_valid) {
- const params = new URLSearchParams(searchParams)
- const { email, workspace_id } = data.data
- params.set('email', encodeURIComponent(email))
- params.set('workspace_id', encodeURIComponent(workspace_id))
- params.set('invite_token', encodeURIComponent(token as string))
- router.replace(`/signin?${params.toString()}`)
- }
- },
- })
+ const { data: checkRes } = useInvitationCheck({
+ ...checkParams.params,
+ token: token || undefined,
+ }, true)
+
+ useEffect(() => {
+ if (checkRes?.is_valid) {
+ const params = new URLSearchParams(searchParams)
+ const { email, workspace_id } = checkRes.data
+ params.set('email', encodeURIComponent(email))
+ params.set('workspace_id', encodeURIComponent(workspace_id))
+ params.set('invite_token', encodeURIComponent(token as string))
+ router.replace(`/signin?${params.toString()}`)
+ }
+ }, [checkRes, router, searchParams, token])
return (
({
updateDatasetSetting: jest.fn(),
}))
-jest.mock('@/service/common', () => ({
- fetchMembers: jest.fn(),
+jest.mock('@/service/use-common', () => ({
+ __esModule: true,
+ ...jest.requireActual('@/service/use-common'),
+ useMembers: jest.fn(),
}))
jest.mock('@/context/app-context', () => ({
@@ -103,7 +106,7 @@ jest.mock('@/app/components/datasets/settings/utils', () => ({
}))
const mockUpdateDatasetSetting = updateDatasetSetting as jest.MockedFunction
-const mockFetchMembers = fetchMembers as jest.MockedFunction
+const mockUseMembers = useMembers as jest.MockedFunction
const createRetrievalConfig = (overrides: Partial = {}): RetrievalConfig => ({
search_method: RETRIEVE_METHOD.semantic,
@@ -192,10 +195,43 @@ const renderWithProviders = (dataset: DataSet) => {
)
}
+const createMemberList = (): DataSet['partial_member_list'] => ([
+ 'member-2',
+])
+
+const renderSettingsModal = async (dataset: DataSet) => {
+ renderWithProviders(dataset)
+ await waitFor(() => expect(mockUseMembers).toHaveBeenCalled())
+}
+
describe('SettingsModal', () => {
beforeEach(() => {
jest.clearAllMocks()
mockIsWorkspaceDatasetOperator = false
+ mockUseMembers.mockReturnValue({
+ data: {
+ accounts: [
+ {
+ id: 'user-1',
+ name: 'User One',
+ email: 'user@example.com',
+ avatar: 'avatar.png',
+ avatar_url: 'avatar.png',
+ status: 'active',
+ role: 'owner',
+ },
+ {
+ id: 'member-2',
+ name: 'Member Two',
+ email: 'member@example.com',
+ avatar: 'avatar.png',
+ avatar_url: 'avatar.png',
+ status: 'active',
+ role: 'editor',
+ },
+ ],
+ },
+ } as ReturnType)
mockUseModelList.mockImplementation((type: ModelTypeEnum) => {
if (type === ModelTypeEnum.rerank) {
return {
@@ -213,261 +249,289 @@ describe('SettingsModal', () => {
mockUseModelListAndDefaultModelAndCurrentProviderAndModel.mockReturnValue({ defaultModel: null, currentModel: null })
mockUseCurrentProviderAndModel.mockReturnValue({ currentProvider: null, currentModel: null })
mockCheckShowMultiModalTip.mockReturnValue(false)
- mockFetchMembers.mockResolvedValue({
- accounts: [
- {
- id: 'user-1',
- name: 'User One',
- email: 'user@example.com',
- avatar: 'avatar.png',
- avatar_url: 'avatar.png',
- status: 'active',
- role: 'owner',
- },
- {
- id: 'member-2',
- name: 'Member Two',
- email: 'member@example.com',
- avatar: 'avatar.png',
- avatar_url: 'avatar.png',
- status: 'active',
- role: 'editor',
- },
- ],
- })
mockUpdateDatasetSetting.mockResolvedValue(createDataset())
})
- it('renders dataset details', async () => {
- renderWithProviders(createDataset())
+ // Rendering and basic field bindings.
+ describe('Rendering', () => {
+ it('should render dataset details when dataset is provided', async () => {
+ // Arrange
+ const dataset = createDataset()
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ // Act
+ await renderSettingsModal(dataset)
- expect(screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')).toHaveValue('Test Dataset')
- expect(screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')).toHaveValue('Description')
- })
-
- it('calls onCancel when cancel is clicked', async () => {
- renderWithProviders(createDataset())
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
-
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
-
- expect(mockOnCancel).toHaveBeenCalledTimes(1)
- })
-
- it('shows external knowledge info for external datasets', async () => {
- const dataset = createDataset({
- provider: 'external',
- external_knowledge_info: {
- external_knowledge_id: 'ext-id-123',
- external_knowledge_api_id: 'ext-api-id-123',
- external_knowledge_api_name: 'External Knowledge API',
- external_knowledge_api_endpoint: 'https://api.external.com',
- },
+ // Assert
+ expect(screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')).toHaveValue('Test Dataset')
+ expect(screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')).toHaveValue('Description')
})
- renderWithProviders(dataset)
+ it('should show external knowledge info when dataset is external', async () => {
+ // Arrange
+ const dataset = createDataset({
+ provider: 'external',
+ external_knowledge_info: {
+ external_knowledge_id: 'ext-id-123',
+ external_knowledge_api_id: 'ext-api-id-123',
+ external_knowledge_api_name: 'External Knowledge API',
+ external_knowledge_api_endpoint: 'https://api.external.com',
+ },
+ })
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ // Act
+ await renderSettingsModal(dataset)
- expect(screen.getByText('External Knowledge API')).toBeInTheDocument()
- expect(screen.getByText('https://api.external.com')).toBeInTheDocument()
- expect(screen.getByText('ext-id-123')).toBeInTheDocument()
- })
-
- it('updates name when user types', async () => {
- renderWithProviders(createDataset())
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
-
- const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
- await userEvent.clear(nameInput)
- await userEvent.type(nameInput, 'New Dataset Name')
-
- expect(nameInput).toHaveValue('New Dataset Name')
- })
-
- it('updates description when user types', async () => {
- renderWithProviders(createDataset())
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
-
- const descriptionInput = screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')
- await userEvent.clear(descriptionInput)
- await userEvent.type(descriptionInput, 'New description')
-
- expect(descriptionInput).toHaveValue('New description')
- })
-
- it('shows and dismisses retrieval change tip when index method changes', async () => {
- const dataset = createDataset({ indexing_technique: IndexingType.ECONOMICAL })
-
- renderWithProviders(dataset)
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
-
- await userEvent.click(screen.getByText('datasetCreation.stepTwo.qualified'))
-
- expect(await screen.findByText('appDebug.datasetConfig.retrieveChangeTip')).toBeInTheDocument()
-
- await userEvent.click(screen.getByLabelText('close-retrieval-change-tip'))
-
- await waitFor(() => {
- expect(screen.queryByText('appDebug.datasetConfig.retrieveChangeTip')).not.toBeInTheDocument()
+ // Assert
+ expect(screen.getByText('External Knowledge API')).toBeInTheDocument()
+ expect(screen.getByText('https://api.external.com')).toBeInTheDocument()
+ expect(screen.getByText('ext-id-123')).toBeInTheDocument()
})
})
- it('requires dataset name before saving', async () => {
- renderWithProviders(createDataset())
+ // User interactions that update visible state.
+ describe('Interactions', () => {
+ it('should call onCancel when cancel button is clicked', async () => {
+ // Arrange
+ const user = userEvent.setup()
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ // Act
+ await renderSettingsModal(createDataset())
+ await user.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
- const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
- await userEvent.clear(nameInput)
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
-
- expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
- type: 'error',
- message: 'datasetSettings.form.nameError',
- }))
- expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
- })
-
- it('requires rerank model when reranking is enabled', async () => {
- mockUseModelList.mockReturnValue({ data: [] })
- const dataset = createDataset({}, createRetrievalConfig({
- reranking_enable: true,
- reranking_model: {
- reranking_provider_name: '',
- reranking_model_name: '',
- },
- }))
-
- renderWithProviders(dataset)
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
-
- expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
- type: 'error',
- message: 'appDebug.datasetConfig.rerankModelRequired',
- }))
- expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
- })
-
- it('saves internal dataset changes', async () => {
- const rerankRetrieval = createRetrievalConfig({
- reranking_enable: true,
- reranking_model: {
- reranking_provider_name: 'rerank-provider',
- reranking_model_name: 'rerank-model',
- },
- })
- const dataset = createDataset({
- retrieval_model: rerankRetrieval,
- retrieval_model_dict: rerankRetrieval,
+ // Assert
+ expect(mockOnCancel).toHaveBeenCalledTimes(1)
})
- renderWithProviders(dataset)
+ it('should update name input when user types', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ await renderSettingsModal(createDataset())
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
- const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
- await userEvent.clear(nameInput)
- await userEvent.type(nameInput, 'Updated Internal Dataset')
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
+ // Act
+ await user.clear(nameInput)
+ await user.type(nameInput, 'New Dataset Name')
- await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
+ // Assert
+ expect(nameInput).toHaveValue('New Dataset Name')
+ })
- expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
- body: expect.objectContaining({
- name: 'Updated Internal Dataset',
- permission: DatasetPermission.allTeamMembers,
- }),
- }))
- expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
- type: 'success',
- message: 'common.actionMsg.modifiedSuccessfully',
- }))
- expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
- name: 'Updated Internal Dataset',
- retrieval_model_dict: expect.objectContaining({
+ it('should update description input when user types', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ await renderSettingsModal(createDataset())
+
+ const descriptionInput = screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')
+
+ // Act
+ await user.clear(descriptionInput)
+ await user.type(descriptionInput, 'New description')
+
+ // Assert
+ expect(descriptionInput).toHaveValue('New description')
+ })
+
+ it('should show and dismiss retrieval change tip when indexing method changes', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ const dataset = createDataset({ indexing_technique: IndexingType.ECONOMICAL })
+
+ // Act
+ await renderSettingsModal(dataset)
+ await user.click(screen.getByText('datasetCreation.stepTwo.qualified'))
+
+ // Assert
+ expect(await screen.findByText('appDebug.datasetConfig.retrieveChangeTip')).toBeInTheDocument()
+
+ // Act
+ await user.click(screen.getByLabelText('close-retrieval-change-tip'))
+
+ // Assert
+ await waitFor(() => {
+ expect(screen.queryByText('appDebug.datasetConfig.retrieveChangeTip')).not.toBeInTheDocument()
+ })
+ })
+
+ it('should open account setting modal when embedding model tip is clicked', async () => {
+ // Arrange
+ const user = userEvent.setup()
+
+ // Act
+ await renderSettingsModal(createDataset())
+ await user.click(screen.getByText('datasetSettings.form.embeddingModelTipLink'))
+
+ // Assert
+ expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({ payload: ACCOUNT_SETTING_TAB.PROVIDER })
+ })
+ })
+
+ // Validation guardrails before saving.
+ describe('Validation', () => {
+ it('should block save when dataset name is empty', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ await renderSettingsModal(createDataset())
+
+ const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
+
+ // Act
+ await user.clear(nameInput)
+ await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
+
+ // Assert
+ expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
+ type: 'error',
+ message: 'datasetSettings.form.nameError',
+ }))
+ expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
+ })
+
+ it('should block save when reranking is enabled without model', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ mockUseModelList.mockReturnValue({ data: [] })
+ const dataset = createDataset({}, createRetrievalConfig({
reranking_enable: true,
- }),
- }))
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ }))
+
+ // Act
+ await renderSettingsModal(dataset)
+ await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
+
+ // Assert
+ expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
+ type: 'error',
+ message: 'appDebug.datasetConfig.rerankModelRequired',
+ }))
+ expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
+ })
})
- it('saves external dataset with partial members and updated retrieval params', async () => {
- const dataset = createDataset({
- provider: 'external',
- permission: DatasetPermission.partialMembers,
- partial_member_list: ['member-2'],
- external_retrieval_model: {
- top_k: 5,
- score_threshold: 0.3,
- score_threshold_enabled: true,
- },
- }, {
- score_threshold_enabled: true,
- score_threshold: 0.8,
+ // Save flows and side effects.
+ describe('Save', () => {
+ it('should save internal dataset changes when form is valid', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ const rerankRetrieval = createRetrievalConfig({
+ reranking_enable: true,
+ reranking_model: {
+ reranking_provider_name: 'rerank-provider',
+ reranking_model_name: 'rerank-model',
+ },
+ })
+ const dataset = createDataset({
+ retrieval_model: rerankRetrieval,
+ retrieval_model_dict: rerankRetrieval,
+ })
+
+ // Act
+ await renderSettingsModal(dataset)
+
+ const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
+ await user.clear(nameInput)
+ await user.type(nameInput, 'Updated Internal Dataset')
+ await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
+
+ // Assert
+ await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
+
+ expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
+ body: expect.objectContaining({
+ name: 'Updated Internal Dataset',
+ permission: DatasetPermission.allTeamMembers,
+ }),
+ }))
+ expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
+ type: 'success',
+ message: 'common.actionMsg.modifiedSuccessfully',
+ }))
+ expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
+ name: 'Updated Internal Dataset',
+ retrieval_model_dict: expect.objectContaining({
+ reranking_enable: true,
+ }),
+ }))
})
- renderWithProviders(dataset)
-
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
-
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
-
- await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
-
- expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
- body: expect.objectContaining({
+ it('should save external dataset changes when partial members configured', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ const dataset = createDataset({
+ provider: 'external',
permission: DatasetPermission.partialMembers,
- external_retrieval_model: expect.objectContaining({
+ partial_member_list: createMemberList(),
+ external_retrieval_model: {
top_k: 5,
- }),
- partial_member_list: [
- {
- user_id: 'member-2',
- role: 'editor',
- },
- ],
- }),
- }))
- expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
- retrieval_model_dict: expect.objectContaining({
+ score_threshold: 0.3,
+ score_threshold_enabled: true,
+ },
+ }, {
score_threshold_enabled: true,
score_threshold: 0.8,
- }),
- }))
- })
+ })
- it('disables save button while saving', async () => {
- mockUpdateDatasetSetting.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
+ // Act
+ await renderSettingsModal(dataset)
+ await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
- renderWithProviders(createDataset())
+ // Assert
+ await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
+ body: expect.objectContaining({
+ permission: DatasetPermission.partialMembers,
+ external_retrieval_model: expect.objectContaining({
+ top_k: 5,
+ }),
+ partial_member_list: [
+ {
+ user_id: 'member-2',
+ role: 'editor',
+ },
+ ],
+ }),
+ }))
+ expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
+ retrieval_model_dict: expect.objectContaining({
+ score_threshold_enabled: true,
+ score_threshold: 0.8,
+ }),
+ }))
+ })
- const saveButton = screen.getByRole('button', { name: 'common.operation.save' })
- await userEvent.click(saveButton)
+ it('should disable save button while saving', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ mockUpdateDatasetSetting.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
- expect(saveButton).toBeDisabled()
- })
+ // Act
+ await renderSettingsModal(createDataset())
- it('shows error toast when save fails', async () => {
- mockUpdateDatasetSetting.mockRejectedValue(new Error('API Error'))
+ const saveButton = screen.getByRole('button', { name: 'common.operation.save' })
+ await user.click(saveButton)
- renderWithProviders(createDataset())
+ // Assert
+ expect(saveButton).toBeDisabled()
+ })
- await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
+ it('should show error toast when save fails', async () => {
+ // Arrange
+ const user = userEvent.setup()
+ mockUpdateDatasetSetting.mockRejectedValue(new Error('API Error'))
- await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
+ // Act
+ await renderSettingsModal(createDataset())
+ await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
- await waitFor(() => {
- expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
+ // Assert
+ await waitFor(() => {
+ expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
+ })
})
})
})
diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
index 8c3e753b22..c191ff5d46 100644
--- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
+++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
@@ -1,6 +1,5 @@
import type { FC } from 'react'
-import { useMemo, useRef, useState } from 'react'
-import { useMount } from 'ahooks'
+import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { isEqual } from 'lodash-es'
import { RiCloseLine } from '@remixicon/react'
@@ -21,10 +20,10 @@ import PermissionSelector from '@/app/components/datasets/settings/permission-se
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
-import { fetchMembers } from '@/service/common'
import type { Member } from '@/models/common'
import { IndexingType } from '@/app/components/datasets/create/step-two'
import { useDocLink } from '@/context/i18n'
+import { useMembers } from '@/service/use-common'
import { checkShowMultiModalTip } from '@/app/components/datasets/settings/utils'
import { RetrievalChangeTip, RetrievalSection } from './retrieval-section'
@@ -63,6 +62,7 @@ const SettingsModal: FC = ({
const [scoreThresholdEnabled, setScoreThresholdEnabled] = useState(localeCurrentDataset?.external_retrieval_model.score_threshold_enabled ?? false)
const [selectedMemberIDs, setSelectedMemberIDs] = useState(currentDataset.partial_member_list || [])
const [memberList, setMemberList] = useState([])
+ const { data: membersData } = useMembers()
const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique)
const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig)
@@ -160,17 +160,12 @@ const SettingsModal: FC = ({
}
}
- const getMembers = async () => {
- const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
- if (!accounts)
+ useEffect(() => {
+ if (!membersData?.accounts)
setMemberList([])
else
- setMemberList(accounts)
- }
-
- useMount(() => {
- getMembers()
- })
+ setMemberList(membersData.accounts)
+ }, [membersData])
const showMultiModalTip = useMemo(() => {
return checkShowMultiModalTip({
diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx
index 2537062e13..4da12319f2 100644
--- a/web/app/components/app/configuration/index.tsx
+++ b/web/app/components/app/configuration/index.tsx
@@ -1,7 +1,6 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import useSWR from 'swr'
import { basePath } from '@/utils/var'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
@@ -72,7 +71,7 @@ import type { Features as FeaturesData, FileUpload } from '@/app/components/base
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import NewFeaturePanel from '@/app/components/base/features/new-feature-panel'
-import { fetchFileUploadConfig } from '@/service/common'
+import { useFileUploadConfig } from '@/service/use-common'
import {
correctModelProvider,
correctToolProvider,
@@ -101,7 +100,7 @@ const Configuration: FC = () => {
showAppConfigureFeaturesModal: state.showAppConfigureFeaturesModal,
setShowAppConfigureFeaturesModal: state.setShowAppConfigureFeaturesModal,
})))
- const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
+ const { data: fileUploadConfigResponse } = useFileUploadConfig()
const latestPublishedAt = useMemo(() => appDetail?.model_config?.updated_at, [appDetail])
const [formattingChanged, setFormattingChanged] = useState(false)
diff --git a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx
index 990e679c79..6f177643ae 100644
--- a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx
+++ b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx
@@ -1,6 +1,5 @@
import type { FC } from 'react'
import { useState } from 'react'
-import useSWR from 'swr'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import FormGeneration from '@/app/components/base/features/new-feature-panel/moderation/form-generation'
@@ -9,7 +8,6 @@ import Button from '@/app/components/base/button'
import EmojiPicker from '@/app/components/base/emoji-picker'
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
-import { fetchCodeBasedExtensionList } from '@/service/common'
import { SimpleSelect } from '@/app/components/base/select'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n-config/language'
@@ -21,6 +19,7 @@ import { useToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import { noop } from 'lodash-es'
import { useDocLink } from '@/context/i18n'
+import { useCodeBasedExtensions } from '@/service/use-common'
const systemTypes = ['api']
type ExternalDataToolModalProps = {
@@ -46,10 +45,7 @@ const ExternalDataToolModal: FC = ({
const { locale } = useContext(I18n)
const [localeData, setLocaleData] = useState(data.type ? data : { ...data, type: 'api' })
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
- const { data: codeBasedExtensionList } = useSWR(
- '/code-based-extension?module=external_data_tool',
- fetchCodeBasedExtensionList,
- )
+ const { data: codeBasedExtensionList } = useCodeBasedExtensions('external_data_tool')
const providers: Provider[] = [
{
diff --git a/web/app/components/base/features/new-feature-panel/moderation/index.tsx b/web/app/components/base/features/new-feature-panel/moderation/index.tsx
index b5bcbca474..abbde2bab9 100644
--- a/web/app/components/base/features/new-feature-panel/moderation/index.tsx
+++ b/web/app/components/base/features/new-feature-panel/moderation/index.tsx
@@ -1,6 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import { produce } from 'immer'
import { useContext } from 'use-context-selector'
import { RiEqualizer2Line } from '@remixicon/react'
@@ -10,9 +9,9 @@ import Button from '@/app/components/base/button'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
import type { OnFeaturesChange } from '@/app/components/base/features/types'
import { FeatureEnum } from '@/app/components/base/features/types'
-import { fetchCodeBasedExtensionList } from '@/service/common'
import { useModalContext } from '@/context/modal-context'
import I18n from '@/context/i18n'
+import { useCodeBasedExtensions } from '@/service/use-common'
type Props = {
disabled?: boolean
@@ -28,10 +27,7 @@ const Moderation = ({
const { locale } = useContext(I18n)
const featuresStore = useFeaturesStore()
const moderation = useFeatures(s => s.features.moderation)
- const { data: codeBasedExtensionList } = useSWR(
- '/code-based-extension?module=moderation',
- fetchCodeBasedExtensionList,
- )
+ const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
const [isHovering, setIsHovering] = useState(false)
const handleOpenModerationSettingModal = () => {
diff --git a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
index 53f5362103..dd9c58c5ab 100644
--- a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
+++ b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
@@ -1,6 +1,5 @@
import type { ChangeEvent, FC } from 'react'
import { useState } from 'react'
-import useSWR from 'swr'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
@@ -13,10 +12,6 @@ import Divider from '@/app/components/base/divider'
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
import type { ModerationConfig, ModerationContentConfig } from '@/models/debug'
import { useToastContext } from '@/app/components/base/toast'
-import {
- fetchCodeBasedExtensionList,
- fetchModelProviders,
-} from '@/service/common'
import type { CodeBasedExtensionItem } from '@/models/common'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n-config/language'
@@ -27,6 +22,7 @@ import { cn } from '@/utils/classnames'
import { noop } from 'lodash-es'
import { useDocLink } from '@/context/i18n'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
+import { useCodeBasedExtensions, useModelProviders } from '@/service/use-common'
const systemTypes = ['openai_moderation', 'keywords', 'api']
@@ -51,21 +47,18 @@ const ModerationSettingModal: FC = ({
const docLink = useDocLink()
const { notify } = useToastContext()
const { locale } = useContext(I18n)
- const { data: modelProviders, isLoading, mutate } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
+ const { data: modelProviders, isPending: isLoading, refetch: refetchModelProviders } = useModelProviders()
const [localeData, setLocaleData] = useState(data)
const { setShowAccountSettingModal } = useModalContext()
const handleOpenSettingsModal = () => {
setShowAccountSettingModal({
payload: ACCOUNT_SETTING_TAB.PROVIDER,
onCancelCallback: () => {
- mutate()
+ refetchModelProviders()
},
})
}
- const { data: codeBasedExtensionList } = useSWR(
- '/code-based-extension?module=moderation',
- fetchCodeBasedExtensionList,
- )
+ const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
const openaiProvider = modelProviders?.data.find(item => item.provider === 'langgenius/openai/openai')
const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled
const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined
diff --git a/web/app/components/datasets/settings/form/index.tsx b/web/app/components/datasets/settings/form/index.tsx
index 5ca85925cc..24942e6249 100644
--- a/web/app/components/datasets/settings/form/index.tsx
+++ b/web/app/components/datasets/settings/form/index.tsx
@@ -1,6 +1,5 @@
'use client'
-import { useCallback, useMemo, useRef, useState } from 'react'
-import { useMount } from 'ahooks'
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import PermissionSelector from '../permission-selector'
import IndexMethod from '../index-method'
@@ -23,7 +22,6 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
-import { fetchMembers } from '@/service/common'
import type { Member } from '@/models/common'
import AppIcon from '@/app/components/base/app-icon'
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
@@ -34,6 +32,7 @@ import Toast from '@/app/components/base/toast'
import { RiAlertFill } from '@remixicon/react'
import { useDocLink } from '@/context/i18n'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
+import { useMembers } from '@/service/use-common'
import { checkShowMultiModalTip } from '../utils'
const rowClass = 'flex gap-x-1'
@@ -79,16 +78,9 @@ const Form = () => {
)
const { data: rerankModelList } = useModelList(ModelTypeEnum.rerank)
const { data: embeddingModelList } = useModelList(ModelTypeEnum.textEmbedding)
+ const { data: membersData } = useMembers()
const previousAppIcon = useRef(DEFAULT_APP_ICON)
- const getMembers = async () => {
- const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
- if (!accounts)
- setMemberList([])
- else
- setMemberList(accounts)
- }
-
const handleOpenAppIconPicker = useCallback(() => {
setShowAppIconPicker(true)
previousAppIcon.current = iconInfo
@@ -119,9 +111,12 @@ const Form = () => {
setScoreThresholdEnabled(data.score_threshold_enabled)
}, [])
- useMount(() => {
- getMembers()
- })
+ useEffect(() => {
+ if (!membersData?.accounts)
+ setMemberList([])
+ else
+ setMemberList(membersData.accounts)
+ }, [membersData])
const invalidDatasetList = useInvalidDatasetList()
const handleSave = async () => {
diff --git a/web/app/components/explore/index.tsx b/web/app/components/explore/index.tsx
index e716de96f1..b9460f8135 100644
--- a/web/app/components/explore/index.tsx
+++ b/web/app/components/explore/index.tsx
@@ -5,10 +5,10 @@ import { useRouter } from 'next/navigation'
import ExploreContext from '@/context/explore-context'
import Sidebar from '@/app/components/explore/sidebar'
import { useAppContext } from '@/context/app-context'
-import { fetchMembers } from '@/service/common'
import type { InstalledApp } from '@/models/explore'
import { useTranslation } from 'react-i18next'
import useDocumentTitle from '@/hooks/use-document-title'
+import { useMembers } from '@/service/use-common'
export type IExploreProps = {
children: React.ReactNode
@@ -24,18 +24,16 @@ const Explore: FC = ({
const [installedApps, setInstalledApps] = useState([])
const [isFetchingInstalledApps, setIsFetchingInstalledApps] = useState(false)
const { t } = useTranslation()
+ const { data: membersData } = useMembers()
useDocumentTitle(t('common.menus.explore'))
useEffect(() => {
- (async () => {
- const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
- if (!accounts)
- return
- const currUser = accounts.find(account => account.id === userProfile.id)
- setHasEditPermission(currUser?.role !== 'normal')
- })()
- }, [])
+ if (!membersData?.accounts)
+ return
+ const currUser = membersData.accounts.find(account => account.id === userProfile.id)
+ setHasEditPermission(currUser?.role !== 'normal')
+ }, [membersData, userProfile.id])
useEffect(() => {
if (isCurrentWorkspaceDatasetOperator)
diff --git a/web/app/components/header/account-setting/Integrations-page/index.tsx b/web/app/components/header/account-setting/Integrations-page/index.tsx
index ae2efcf3d1..460cc2ed5a 100644
--- a/web/app/components/header/account-setting/Integrations-page/index.tsx
+++ b/web/app/components/header/account-setting/Integrations-page/index.tsx
@@ -1,11 +1,10 @@
'use client'
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import Link from 'next/link'
import s from './index.module.css'
import { cn } from '@/utils/classnames'
-import { fetchAccountIntegrates } from '@/service/common'
+import { useAccountIntegrates } from '@/service/use-common'
const titleClassName = `
mb-2 text-sm font-medium text-gray-900
@@ -25,33 +24,38 @@ export default function IntegrationsPage() {
},
}
- const { data } = useSWR({ url: '/account/integrates' }, fetchAccountIntegrates)
- const integrates = data?.data?.length ? data.data : []
+ const { data } = useAccountIntegrates()
+ const integrates = data?.data ?? []
return (
<>
{t('common.integrations.connected')}
{
- integrates.map(integrate => (
-
-
-
-
{integrateMap[integrate.provider].name}
-
{integrateMap[integrate.provider].description}
+ integrates.map((integrate) => {
+ const info = integrateMap[integrate.provider]
+ if (!info)
+ return null
+ return (
+
+
+
+
{info.name}
+
{info.description}
+
+ {
+ !integrate.is_bound && (
+
+ {t('common.integrations.connect')}
+
+ )
+ }
- {
- !integrate.is_bound && (
-
- {t('common.integrations.connect')}
-
- )
- }
-
- ))
+ )
+ })
}
{/*
diff --git a/web/app/components/header/account-setting/api-based-extension-page/index.tsx b/web/app/components/header/account-setting/api-based-extension-page/index.tsx
index d16c4f2ded..24dfce5b90 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/index.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/index.tsx
@@ -1,5 +1,4 @@
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import {
RiAddLine,
} from '@remixicon/react'
@@ -7,15 +6,12 @@ import Item from './item'
import Empty from './empty'
import Button from '@/app/components/base/button'
import { useModalContext } from '@/context/modal-context'
-import { fetchApiBasedExtensionList } from '@/service/common'
+import { useApiBasedExtensions } from '@/service/use-common'
const ApiBasedExtensionPage = () => {
const { t } = useTranslation()
const { setShowApiBasedExtensionModal } = useModalContext()
- const { data, mutate, isLoading } = useSWR(
- '/api-based-extension',
- fetchApiBasedExtensionList,
- )
+ const { data, refetch: mutate, isPending: isLoading } = useApiBasedExtensions()
const handleOpenApiBasedExtensionModal = () => {
setShowApiBasedExtensionModal({
diff --git a/web/app/components/header/account-setting/api-based-extension-page/selector.tsx b/web/app/components/header/account-setting/api-based-extension-page/selector.tsx
index 549b5e7910..9da3745f2f 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/selector.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/selector.tsx
@@ -1,6 +1,5 @@
import type { FC } from 'react'
import { useState } from 'react'
-import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import {
RiAddLine,
@@ -15,8 +14,8 @@ import {
ArrowUpRight,
} from '@/app/components/base/icons/src/vender/line/arrows'
import { useModalContext } from '@/context/modal-context'
-import { fetchApiBasedExtensionList } from '@/service/common'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
+import { useApiBasedExtensions } from '@/service/use-common'
type ApiBasedExtensionSelectorProps = {
value: string
@@ -33,10 +32,7 @@ const ApiBasedExtensionSelector: FC
= ({
setShowAccountSettingModal,
setShowApiBasedExtensionModal,
} = useModalContext()
- const { data, mutate } = useSWR(
- '/api-based-extension',
- fetchApiBasedExtensionList,
- )
+ const { data, refetch: mutate } = useApiBasedExtensions()
const handleSelect = (id: string) => {
onChange(id)
setOpen(false)
diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx
index 065ef91eba..68fd52d0a4 100644
--- a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx
+++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx
@@ -1,16 +1,15 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
-import useSWR from 'swr'
import Panel from '../panel'
import { DataSourceType } from '../panel/types'
import type { DataSourceNotion as TDataSourceNotion } from '@/models/common'
import { useAppContext } from '@/context/app-context'
-import { fetchNotionConnection } from '@/service/common'
import NotionIcon from '@/app/components/base/notion-icon'
import { noop } from 'lodash-es'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
+import { useDataSourceIntegrates, useNotionConnection } from '@/service/use-common'
const Icon: FC<{
src: string
@@ -26,7 +25,7 @@ const Icon: FC<{
)
}
type Props = {
- workspaces: TDataSourceNotion[]
+ workspaces?: TDataSourceNotion[]
}
const DataSourceNotion: FC = ({
@@ -34,10 +33,14 @@ const DataSourceNotion: FC = ({
}) => {
const { isCurrentWorkspaceManager } = useAppContext()
const [canConnectNotion, setCanConnectNotion] = useState(false)
- const { data } = useSWR(canConnectNotion ? '/oauth/data-source/notion' : null, fetchNotionConnection)
+ const { data: integrates } = useDataSourceIntegrates({
+ initialData: workspaces ? { data: workspaces } : undefined,
+ })
+ const { data } = useNotionConnection(canConnectNotion)
const { t } = useTranslation()
- const connected = !!workspaces.length
+ const resolvedWorkspaces = integrates?.data ?? []
+ const connected = !!resolvedWorkspaces.length
const handleConnectNotion = () => {
if (!isCurrentWorkspaceManager)
@@ -74,7 +77,7 @@ const DataSourceNotion: FC = ({
onConfigure={handleConnectNotion}
readOnly={!isCurrentWorkspaceManager}
isSupportList
- configuredList={workspaces.map(workspace => ({
+ configuredList={resolvedWorkspaces.map(workspace => ({
id: workspace.id,
logo: ({ className }: { className: string }) => (
{
Toast.notify({
type: 'success',
message: t('common.api.success'),
})
- mutate({ url: 'data-source/integrates' })
+ invalidateDataSourceIntegrates()
}
const handleSync = async () => {
await syncDataSourceNotion({ url: `/oauth/data-source/notion/${payload.id}/sync` })
diff --git a/web/app/components/header/account-setting/members-page/index.tsx b/web/app/components/header/account-setting/members-page/index.tsx
index 6f75372ed9..e951e5b85a 100644
--- a/web/app/components/header/account-setting/members-page/index.tsx
+++ b/web/app/components/header/account-setting/members-page/index.tsx
@@ -1,6 +1,5 @@
'use client'
import { useState } from 'react'
-import useSWR from 'swr'
import { useContext } from 'use-context-selector'
import { RiUserAddLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
@@ -10,7 +9,6 @@ import EditWorkspaceModal from './edit-workspace-modal'
import TransferOwnershipModal from './transfer-ownership-modal'
import Operation from './operation'
import TransferOwnership from './operation/transfer-ownership'
-import { fetchMembers } from '@/service/common'
import I18n from '@/context/i18n'
import { useAppContext } from '@/context/app-context'
import Avatar from '@/app/components/base/avatar'
@@ -26,6 +24,7 @@ import Tooltip from '@/app/components/base/tooltip'
import { RiPencilLine } from '@remixicon/react'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
+import { useMembers } from '@/service/use-common'
const MembersPage = () => {
const { t } = useTranslation()
@@ -39,13 +38,7 @@ const MembersPage = () => {
const { locale } = useContext(I18n)
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
- const { data, mutate } = useSWR(
- {
- url: '/workspaces/current/members',
- params: {},
- },
- fetchMembers,
- )
+ const { data, refetch } = useMembers()
const { systemFeatures } = useGlobalPublicStore()
const { formatTimeFromNow } = useFormatTimeFromNow()
const [inviteModalVisible, setInviteModalVisible] = useState(false)
@@ -140,7 +133,7 @@ const MembersPage = () => {
{RoleMap[account.role] || RoleMap.normal}
)}
{isCurrentWorkspaceOwner && account.role !== 'owner' && (
-
+
)}
{!isCurrentWorkspaceOwner && (
{RoleMap[account.role] || RoleMap.normal}
@@ -160,7 +153,7 @@ const MembersPage = () => {
onSend={(invitationResults) => {
setInvitedModalVisible(true)
setInvitationResults(invitationResults)
- mutate()
+ refetch()
}}
/>
)
diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
index 78988db071..70c8e300f0 100644
--- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
+++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
@@ -2,15 +2,14 @@
import type { FC } from 'react'
import React, { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import {
RiArrowDownSLine,
} from '@remixicon/react'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
import Avatar from '@/app/components/base/avatar'
import Input from '@/app/components/base/input'
-import { fetchMembers } from '@/service/common'
import { cn } from '@/utils/classnames'
+import { useMembers } from '@/service/use-common'
type Props = {
value?: any
@@ -27,13 +26,7 @@ const MemberSelector: FC = ({
const [open, setOpen] = useState(false)
const [searchValue, setSearchValue] = useState('')
- const { data } = useSWR(
- {
- url: '/workspaces/current/members',
- params: {},
- },
- fetchMembers,
- )
+ const { data } = useMembers()
const currentValue = useMemo(() => {
if (!data?.accounts) return null
diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.spec.ts b/web/app/components/header/account-setting/model-provider-page/hooks.spec.ts
index b7a56f7b60..e1f42aa56f 100644
--- a/web/app/components/header/account-setting/model-provider-page/hooks.spec.ts
+++ b/web/app/components/header/account-setting/model-provider-page/hooks.spec.ts
@@ -3,15 +3,21 @@ import { useLanguage } from './hooks'
import { useContext } from 'use-context-selector'
import { after } from 'node:test'
-jest.mock('swr', () => ({
- __esModule: true,
- default: jest.fn(), // mock useSWR
- useSWRConfig: jest.fn(),
+jest.mock('@tanstack/react-query', () => ({
+ useQuery: jest.fn(),
+ useQueryClient: jest.fn(() => ({
+ invalidateQueries: jest.fn(),
+ })),
}))
// mock use-context-selector
jest.mock('use-context-selector', () => ({
useContext: jest.fn(),
+ createContext: () => ({
+ Provider: ({ children }: any) => children,
+ Consumer: ({ children }: any) => children(null),
+ }),
+ useContextSelector: jest.fn(),
}))
// mock service/common functions
@@ -19,10 +25,15 @@ jest.mock('@/service/common', () => ({
fetchDefaultModal: jest.fn(),
fetchModelList: jest.fn(),
fetchModelProviderCredentials: jest.fn(),
- fetchModelProviders: jest.fn(),
getPayUrl: jest.fn(),
}))
+jest.mock('@/service/use-common', () => ({
+ commonQueryKeys: {
+ modelProviders: ['common', 'model-providers'],
+ },
+}))
+
// mock context hooks
jest.mock('@/context/i18n', () => ({
__esModule: true,
diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts
index 0ffd1df9de..ff5899f01c 100644
--- a/web/app/components/header/account-setting/model-provider-page/hooks.ts
+++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts
@@ -4,7 +4,7 @@ import {
useMemo,
useState,
} from 'react'
-import useSWR, { useSWRConfig } from 'swr'
+import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useContext } from 'use-context-selector'
import type {
Credential,
@@ -27,9 +27,9 @@ import {
fetchDefaultModal,
fetchModelList,
fetchModelProviderCredentials,
- fetchModelProviders,
getPayUrl,
} from '@/service/common'
+import { commonQueryKeys } from '@/service/use-common'
import { useProviderContext } from '@/context/provider-context'
import {
useMarketplacePlugins,
@@ -81,17 +81,23 @@ export const useProviderCredentialsAndLoadBalancing = (
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
credentialId?: string,
) => {
- const { data: predefinedFormSchemasValue, mutate: mutatePredefined, isLoading: isPredefinedLoading } = useSWR(
- (configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
- ? `/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`
- : null,
- fetchModelProviderCredentials,
+ const queryClient = useQueryClient()
+ const predefinedEnabled = configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && !!credentialId
+ const customEnabled = configurationMethod === ConfigurationMethodEnum.customizableModel && !!currentCustomConfigurationModelFixedFields && !!credentialId
+
+ const { data: predefinedFormSchemasValue, isPending: isPredefinedLoading } = useQuery(
+ {
+ queryKey: ['model-providers', 'credentials', provider, credentialId],
+ queryFn: () => fetchModelProviderCredentials(`/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`),
+ enabled: predefinedEnabled,
+ },
)
- const { data: customFormSchemasValue, mutate: mutateCustomized, isLoading: isCustomizedLoading } = useSWR(
- (configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields && credentialId)
- ? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`
- : null,
- fetchModelProviderCredentials,
+ const { data: customFormSchemasValue, isPending: isCustomizedLoading } = useQuery(
+ {
+ queryKey: ['model-providers', 'models', 'credentials', provider, currentCustomConfigurationModelFixedFields?.__model_type, currentCustomConfigurationModelFixedFields?.__model_name, credentialId],
+ queryFn: () => fetchModelProviderCredentials(`/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`),
+ enabled: customEnabled,
+ },
)
const credentials = useMemo(() => {
@@ -112,9 +118,11 @@ export const useProviderCredentialsAndLoadBalancing = (
])
const mutate = useMemo(() => () => {
- mutatePredefined()
- mutateCustomized()
- }, [mutateCustomized, mutatePredefined])
+ if (predefinedEnabled)
+ queryClient.invalidateQueries({ queryKey: ['model-providers', 'credentials', provider, credentialId] })
+ if (customEnabled)
+ queryClient.invalidateQueries({ queryKey: ['model-providers', 'models', 'credentials', provider, currentCustomConfigurationModelFixedFields?.__model_type, currentCustomConfigurationModelFixedFields?.__model_name, credentialId] })
+ }, [customEnabled, credentialId, currentCustomConfigurationModelFixedFields?.__model_name, currentCustomConfigurationModelFixedFields?.__model_type, predefinedEnabled, provider, queryClient])
return {
credentials,
@@ -129,22 +137,28 @@ export const useProviderCredentialsAndLoadBalancing = (
}
export const useModelList = (type: ModelTypeEnum) => {
- const { data, mutate, isLoading } = useSWR(`/workspaces/current/models/model-types/${type}`, fetchModelList)
+ const { data, refetch, isPending } = useQuery({
+ queryKey: commonQueryKeys.modelList(type),
+ queryFn: () => fetchModelList(`/workspaces/current/models/model-types/${type}`),
+ })
return {
data: data?.data || [],
- mutate,
- isLoading,
+ mutate: refetch,
+ isLoading: isPending,
}
}
export const useDefaultModel = (type: ModelTypeEnum) => {
- const { data, mutate, isLoading } = useSWR(`/workspaces/current/default-model?model_type=${type}`, fetchDefaultModal)
+ const { data, refetch, isPending } = useQuery({
+ queryKey: commonQueryKeys.defaultModel(type),
+ queryFn: () => fetchDefaultModal(`/workspaces/current/default-model?model_type=${type}`),
+ })
return {
data: data?.data,
- mutate,
- isLoading,
+ mutate: refetch,
+ isLoading: isPending,
}
}
@@ -200,11 +214,11 @@ export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: Mode
}
export const useUpdateModelList = () => {
- const { mutate } = useSWRConfig()
+ const queryClient = useQueryClient()
const updateModelList = useCallback((type: ModelTypeEnum) => {
- mutate(`/workspaces/current/models/model-types/${type}`)
- }, [mutate])
+ queryClient.invalidateQueries({ queryKey: commonQueryKeys.modelList(type) })
+ }, [queryClient])
return updateModelList
}
@@ -230,22 +244,12 @@ export const useAnthropicBuyQuota = () => {
return handleGetPayUrl
}
-export const useModelProviders = () => {
- const { data: providersData, mutate, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
-
- return {
- data: providersData?.data || [],
- mutate,
- isLoading,
- }
-}
-
export const useUpdateModelProviders = () => {
- const { mutate } = useSWRConfig()
+ const queryClient = useQueryClient()
const updateModelProviders = useCallback(() => {
- mutate('/workspaces/current/model-providers')
- }, [mutate])
+ queryClient.invalidateQueries({ queryKey: commonQueryKeys.modelProviders })
+ }, [queryClient])
return updateModelProviders
}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
index 016b8b0fd6..e7323c86e6 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
@@ -3,7 +3,6 @@ import type {
ReactNode,
} from 'react'
import { useMemo, useState } from 'react'
-import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import type {
DefaultModel,
@@ -26,11 +25,11 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { fetchModelParameterRules } from '@/service/common'
import Loading from '@/app/components/base/loading'
import { useProviderContext } from '@/context/provider-context'
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config'
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
+import { useModelParameterRules } from '@/service/use-common'
export type ModelParameterModalProps = {
popupClassName?: string
@@ -69,7 +68,7 @@ const ModelParameterModal: FC = ({
const { t } = useTranslation()
const { isAPIKeySet } = useProviderContext()
const [open, setOpen] = useState(false)
- const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules)
+ const { data: parameterRulesData, isPending: isLoading } = useModelParameterRules(provider, modelId)
const {
currentProvider,
currentModel,
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
index 0282d36214..911485edf6 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
@@ -5,6 +5,7 @@ import type { ModelItem, ModelProvider } from '../declarations'
import { ModelStatusEnum } from '../declarations'
import ModelIcon from '../model-icon'
import ModelName from '../model-name'
+import { useUpdateModelList } from '../hooks'
import { cn } from '@/utils/classnames'
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import Switch from '@/app/components/base/switch'
@@ -20,21 +21,25 @@ export type ModelListItemProps = {
model: ModelItem
provider: ModelProvider
isConfigurable: boolean
+ onChange?: (provider: string) => void
onModifyLoadBalancing?: (model: ModelItem) => void
}
-const ModelListItem = ({ model, provider, isConfigurable, onModifyLoadBalancing }: ModelListItemProps) => {
+const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoadBalancing }: ModelListItemProps) => {
const { t } = useTranslation()
const { plan } = useProviderContext()
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
const { isCurrentWorkspaceManager } = useAppContext()
+ const updateModelList = useUpdateModelList()
const toggleModelEnablingStatus = useCallback(async (enabled: boolean) => {
if (enabled)
await enableModel(`/workspaces/current/model-providers/${provider.provider}/models/enable`, { model: model.model, model_type: model.model_type })
else
await disableModel(`/workspaces/current/model-providers/${provider.provider}/models/disable`, { model: model.model, model_type: model.model_type })
- }, [model.model, model.model_type, provider.provider])
+ updateModelList(model.model_type)
+ onChange?.(provider.provider)
+ }, [model.model, model.model_type, onChange, provider.provider, updateModelList])
const { run: debouncedToggleModelEnablingStatus } = useDebounceFn(toggleModelEnablingStatus, { wait: 500 })
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx
index 2e008a0b35..1efa9628ac 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx
@@ -91,6 +91,7 @@ const ModelList: FC = ({
model,
provider,
isConfigurable,
+ onChange,
onModifyLoadBalancing,
}}
/>
diff --git a/web/app/components/header/account-setting/plugin-page/index.tsx b/web/app/components/header/account-setting/plugin-page/index.tsx
index bf404b05bb..5195ca9501 100644
--- a/web/app/components/header/account-setting/plugin-page/index.tsx
+++ b/web/app/components/header/account-setting/plugin-page/index.tsx
@@ -1,14 +1,13 @@
-import useSWR from 'swr'
import { LockClosedIcon } from '@heroicons/react/24/solid'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import SerpapiPlugin from './SerpapiPlugin'
-import { fetchPluginProviders } from '@/service/common'
import type { PluginProvider } from '@/models/common'
+import { usePluginProviders } from '@/service/use-common'
const PluginPage = () => {
const { t } = useTranslation()
- const { data: plugins, mutate } = useSWR('/workspaces/current/tool-providers', fetchPluginProviders)
+ const { data: plugins, refetch: mutate } = usePluginProviders()
const Plugin_MAP: Record React.JSX.Element> = {
serpapi: (plugin: PluginProvider) => mutate()} />,
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx
index b05be5005a..e741ec1772 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx
@@ -1,5 +1,4 @@
import React, { useMemo } from 'react'
-import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import PresetsParameter from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter'
import ParameterItem from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item'
@@ -9,9 +8,9 @@ import type {
ModelParameterRule,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { ParameterValue } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item'
-import { fetchModelParameterRules } from '@/service/common'
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config'
import { cn } from '@/utils/classnames'
+import { useModelParameterRules } from '@/service/use-common'
type Props = {
isAdvancedMode: boolean
@@ -29,11 +28,7 @@ const LLMParamsPanel = ({
onCompletionParamsChange,
}: Props) => {
const { t } = useTranslation()
- const { data: parameterRulesData, isLoading } = useSWR(
- (provider && modelId)
- ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`
- : null, fetchModelParameterRules,
- )
+ const { data: parameterRulesData, isPending: isLoading } = useModelParameterRules(provider, modelId)
const parameterRules: ModelParameterRule[] = useMemo(() => {
return parameterRulesData?.data || []
diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx
index 0dccf23e9e..ce41777471 100644
--- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx
+++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx
@@ -1,7 +1,6 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
-import useSWR from 'swr'
import { produce } from 'immer'
import { useTranslation } from 'react-i18next'
import type { UploadFileSetting } from '../../../types'
@@ -11,9 +10,9 @@ import FileTypeItem from './file-type-item'
import InputNumberWithSlider from './input-number-with-slider'
import Field from '@/app/components/app/configuration/config-var/config-modal/field'
import { TransferMethod } from '@/types/app'
-import { fetchFileUploadConfig } from '@/service/common'
import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks'
import { formatFileSize } from '@/utils/format'
+import { useFileUploadConfig } from '@/service/use-common'
type Props = {
payload: UploadFileSetting
@@ -38,7 +37,7 @@ const FileUploadSetting: FC = ({
allowed_file_types,
allowed_file_extensions,
} = payload
- const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
+ const { data: fileUploadConfigResponse } = useFileUploadConfig()
const {
imgSizeLimit,
docSizeLimit,
diff --git a/web/app/forgot-password/ChangePasswordForm.tsx b/web/app/forgot-password/ChangePasswordForm.tsx
index b2424c85fc..66119ea691 100644
--- a/web/app/forgot-password/ChangePasswordForm.tsx
+++ b/web/app/forgot-password/ChangePasswordForm.tsx
@@ -1,30 +1,28 @@
'use client'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
import { useSearchParams } from 'next/navigation'
import { basePath } from '@/utils/var'
import { cn } from '@/utils/classnames'
import { CheckCircleIcon } from '@heroicons/react/24/solid'
import Input from '../components/base/input'
import Button from '@/app/components/base/button'
-import { changePasswordWithToken, verifyForgotPasswordToken } from '@/service/common'
+import { changePasswordWithToken } from '@/service/common'
import Toast from '@/app/components/base/toast'
import Loading from '@/app/components/base/loading'
import { validPassword } from '@/config'
+import { useVerifyForgotPasswordToken } from '@/service/use-common'
const ChangePasswordForm = () => {
const { t } = useTranslation()
const searchParams = useSearchParams()
const token = searchParams.get('token')
+ const isTokenMissing = !token
- const verifyTokenParams = {
- url: '/forgot-password/validity',
- body: { token },
- }
- const { data: verifyTokenRes, mutate: revalidateToken } = useSWR(verifyTokenParams, verifyForgotPasswordToken, {
- revalidateOnFocus: false,
- })
+ const {
+ data: verifyTokenRes,
+ refetch: revalidateToken,
+ } = useVerifyForgotPasswordToken(token)
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
@@ -82,8 +80,8 @@ const ChangePasswordForm = () => {
'md:px-[108px]',
)
}>
- {!verifyTokenRes && }
- {verifyTokenRes && !verifyTokenRes.is_valid && (
+ {!isTokenMissing && !verifyTokenRes && }
+ {(isTokenMissing || (verifyTokenRes && !verifyTokenRes.is_valid)) && (
🤷♂️
diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx
index cbd37f51f6..de8d6c60ea 100644
--- a/web/app/signin/invite-settings/page.tsx
+++ b/web/app/signin/invite-settings/page.tsx
@@ -5,7 +5,6 @@ import { useCallback, useState } from 'react'
import Link from 'next/link'
import { useContext } from 'use-context-selector'
import { useRouter, useSearchParams } from 'next/navigation'
-import useSWR from 'swr'
import { RiAccountCircleLine } from '@remixicon/react'
import Input from '@/app/components/base/input'
import { SimpleSelect } from '@/app/components/base/select'
@@ -13,12 +12,13 @@ import Button from '@/app/components/base/button'
import { timezones } from '@/utils/timezone'
import { LanguagesSupported, languages } from '@/i18n-config/language'
import I18n from '@/context/i18n'
-import { activateMember, invitationCheck } from '@/service/common'
+import { activateMember } from '@/service/common'
import Loading from '@/app/components/base/loading'
import Toast from '@/app/components/base/toast'
import { noop } from 'lodash-es'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
+import { useInvitationCheck } from '@/service/use-common'
export default function InviteSettingsPage() {
const { t } = useTranslation()
@@ -38,9 +38,7 @@ export default function InviteSettingsPage() {
token,
},
}
- const { data: checkRes, mutate: recheck } = useSWR(checkParams, invitationCheck, {
- revalidateOnFocus: false,
- })
+ const { data: checkRes, refetch: recheck } = useInvitationCheck(checkParams.params, !!token)
const handleActivate = useCallback(async () => {
try {
diff --git a/web/app/signin/one-more-step.tsx b/web/app/signin/one-more-step.tsx
index 3293caa8f5..4b20f85681 100644
--- a/web/app/signin/one-more-step.tsx
+++ b/web/app/signin/one-more-step.tsx
@@ -1,8 +1,7 @@
'use client'
-import React, { type Reducer, useEffect, useReducer } from 'react'
+import React, { type Reducer, useReducer } from 'react'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
-import useSWR from 'swr'
import { useRouter, useSearchParams } from 'next/navigation'
import Input from '../components/base/input'
import Button from '@/app/components/base/button'
@@ -10,12 +9,11 @@ import Tooltip from '@/app/components/base/tooltip'
import { SimpleSelect } from '@/app/components/base/select'
import { timezones } from '@/utils/timezone'
import { LanguagesSupported, languages } from '@/i18n-config/language'
-import { oneMoreStep } from '@/service/common'
import Toast from '@/app/components/base/toast'
import { useDocLink } from '@/context/i18n'
+import { useOneMoreStep } from '@/service/use-common'
type IState = {
- formState: 'processing' | 'error' | 'success' | 'initial'
invitation_code: string
interface_language: string
timezone: string
@@ -26,7 +24,6 @@ type IAction
| { type: 'invitation_code', value: string }
| { type: 'interface_language', value: string }
| { type: 'timezone', value: string }
- | { type: 'formState', value: 'processing' }
const reducer: Reducer
= (state: IState, action: IAction) => {
switch (action.type) {
@@ -36,11 +33,8 @@ const reducer: Reducer = (state: IState, action: IAction) => {
return { ...state, interface_language: action.value }
case 'timezone':
return { ...state, timezone: action.value }
- case 'formState':
- return { ...state, formState: action.value }
case 'failed':
return {
- formState: 'initial',
invitation_code: '',
interface_language: 'en-US',
timezone: 'Asia/Shanghai',
@@ -57,30 +51,29 @@ const OneMoreStep = () => {
const searchParams = useSearchParams()
const [state, dispatch] = useReducer(reducer, {
- formState: 'initial',
invitation_code: searchParams.get('invitation_code') || '',
interface_language: 'en-US',
timezone: 'Asia/Shanghai',
})
- const { data, error } = useSWR(state.formState === 'processing'
- ? {
- url: '/account/init',
- body: {
+ const { mutateAsync: submitOneMoreStep, isPending } = useOneMoreStep()
+
+ const handleSubmit = async () => {
+ if (isPending)
+ return
+ try {
+ await submitOneMoreStep({
invitation_code: state.invitation_code,
interface_language: state.interface_language,
timezone: state.timezone,
- },
+ })
+ router.push('/apps')
}
- : null, oneMoreStep)
-
- useEffect(() => {
- if (error && error.status === 400) {
- Toast.notify({ type: 'error', message: t('login.invalidInvitationCode') })
+ catch (error: any) {
+ if (error && error.status === 400)
+ Toast.notify({ type: 'error', message: t('login.invalidInvitationCode') })
dispatch({ type: 'failed', payload: null })
}
- if (data)
- router.push('/apps')
- }, [data, error])
+ }
return (
<>
@@ -151,10 +144,8 @@ const OneMoreStep = () => {
diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx
index 426ef2217e..48d67c3611 100644
--- a/web/context/app-context.tsx
+++ b/web/context/app-context.tsx
@@ -1,10 +1,14 @@
'use client'
-import { useCallback, useEffect, useMemo, useState } from 'react'
-import useSWR from 'swr'
+import { useCallback, useEffect, useMemo } from 'react'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import type { FC, ReactNode } from 'react'
-import { fetchCurrentWorkspace, fetchLangGeniusVersion, fetchUserProfile } from '@/service/common'
+import { useQueryClient } from '@tanstack/react-query'
+import {
+ useCurrentWorkspace,
+ useLangGeniusVersion,
+ useUserProfile,
+} from '@/service/use-common'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
import { noop } from 'lodash-es'
@@ -79,48 +83,44 @@ export type AppContextProviderProps = {
}
export const AppContextProvider: FC = ({ children }) => {
+ const queryClient = useQueryClient()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
- const { data: userProfileResponse, mutate: mutateUserProfile, error: userProfileError } = useSWR({ url: '/account/profile', params: {} }, fetchUserProfile)
- const { data: currentWorkspaceResponse, mutate: mutateCurrentWorkspace, isLoading: isLoadingCurrentWorkspace } = useSWR({ url: '/workspaces/current', params: {} }, fetchCurrentWorkspace)
+ const { data: userProfileResp } = useUserProfile()
+ const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace } = useCurrentWorkspace()
+ const langGeniusVersionQuery = useLangGeniusVersion(
+ userProfileResp?.meta.currentVersion,
+ !systemFeatures.branding.enabled,
+ )
+
+ const userProfile = useMemo(() => userProfileResp?.profile || userProfilePlaceholder, [userProfileResp?.profile])
+ const currentWorkspace = useMemo(() => currentWorkspaceResp || initialWorkspaceInfo, [currentWorkspaceResp])
+ const langGeniusVersionInfo = useMemo(() => {
+ if (!userProfileResp?.meta?.currentVersion || !langGeniusVersionQuery.data)
+ return initialLangGeniusVersionInfo
+
+ const current_version = userProfileResp.meta.currentVersion
+ const current_env = userProfileResp.meta.currentEnv || ''
+ const versionData = langGeniusVersionQuery.data
+ return {
+ ...versionData,
+ current_version,
+ latest_version: versionData.version,
+ current_env,
+ }
+ }, [langGeniusVersionQuery.data, userProfileResp?.meta])
- const [userProfile, setUserProfile] = useState(userProfilePlaceholder)
- const [langGeniusVersionInfo, setLangGeniusVersionInfo] = useState(initialLangGeniusVersionInfo)
- const [currentWorkspace, setCurrentWorkspace] = useState(initialWorkspaceInfo)
const isCurrentWorkspaceManager = useMemo(() => ['owner', 'admin'].includes(currentWorkspace.role), [currentWorkspace.role])
const isCurrentWorkspaceOwner = useMemo(() => currentWorkspace.role === 'owner', [currentWorkspace.role])
const isCurrentWorkspaceEditor = useMemo(() => ['owner', 'admin', 'editor'].includes(currentWorkspace.role), [currentWorkspace.role])
const isCurrentWorkspaceDatasetOperator = useMemo(() => currentWorkspace.role === 'dataset_operator', [currentWorkspace.role])
- const updateUserProfileAndVersion = useCallback(async () => {
- if (userProfileResponse && !userProfileResponse.bodyUsed) {
- try {
- const result = await userProfileResponse.json()
- setUserProfile(result)
- if (!systemFeatures.branding.enabled) {
- const current_version = userProfileResponse.headers.get('x-version')
- const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env')
- const versionData = await fetchLangGeniusVersion({ url: '/version', params: { current_version } })
- setLangGeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env })
- }
- }
- catch (error) {
- console.error('Failed to update user profile:', error)
- if (userProfile.id === '')
- setUserProfile(userProfilePlaceholder)
- }
- }
- else if (userProfileError && userProfile.id === '') {
- setUserProfile(userProfilePlaceholder)
- }
- }, [userProfileResponse, userProfileError, userProfile.id])
- useEffect(() => {
- updateUserProfileAndVersion()
- }, [updateUserProfileAndVersion, userProfileResponse])
+ const mutateUserProfile = useCallback(() => {
+ queryClient.invalidateQueries({ queryKey: ['common', 'user-profile'] })
+ }, [queryClient])
- useEffect(() => {
- if (currentWorkspaceResponse)
- setCurrentWorkspace(currentWorkspaceResponse)
- }, [currentWorkspaceResponse])
+ const mutateCurrentWorkspace = useCallback(() => {
+ queryClient.invalidateQueries({ queryKey: ['common', 'current-workspace'] })
+ }, [queryClient])
// #region Zendesk conversation fields
useEffect(() => {
diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx
index 70944d85f1..e1739853c6 100644
--- a/web/context/provider-context.tsx
+++ b/web/context/provider-context.tsx
@@ -1,15 +1,15 @@
'use client'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
-import useSWR from 'swr'
import { useEffect, useState } from 'react'
import dayjs from 'dayjs'
import { useTranslation } from 'react-i18next'
+import { useQueryClient } from '@tanstack/react-query'
import {
- fetchModelList,
- fetchModelProviders,
- fetchSupportRetrievalMethods,
-} from '@/service/common'
+ useModelListByType,
+ useModelProviders,
+ useSupportRetrievalMethods,
+} from '@/service/use-common'
import {
CurrentSystemQuotaTypeEnum,
ModelStatusEnum,
@@ -114,10 +114,10 @@ type ProviderContextProviderProps = {
export const ProviderContextProvider = ({
children,
}: ProviderContextProviderProps) => {
- const { data: providersData, mutate: refreshModelProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
- const fetchModelListUrlPrefix = '/workspaces/current/models/model-types/'
- const { data: textGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList)
- const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods)
+ const queryClient = useQueryClient()
+ const { data: providersData } = useModelProviders()
+ const { data: textGenerationModelList } = useModelListByType(ModelTypeEnum.textGeneration)
+ const { data: supportRetrievalMethods } = useSupportRetrievalMethods()
const [plan, setPlan] = useState(defaultPlan)
const [isFetchedPlan, setIsFetchedPlan] = useState(false)
@@ -139,6 +139,10 @@ export const ProviderContextProvider = ({
const [isAllowTransferWorkspace, setIsAllowTransferWorkspace] = useState(false)
const [isAllowPublishAsCustomKnowledgePipelineTemplate, setIsAllowPublishAsCustomKnowledgePipelineTemplate] = useState(false)
+ const refreshModelProviders = () => {
+ queryClient.invalidateQueries({ queryKey: ['common', 'model-providers'] })
+ }
+
const fetchPlan = async () => {
try {
const data = await fetchCurrentPlanInfo()
@@ -226,7 +230,7 @@ export const ProviderContextProvider = ({
modelProviders: providersData?.data || [],
refreshModelProviders,
textGenerationModelList: textGenerationModelList?.data || [],
- isAPIKeySet: !!textGenerationModelList?.data.some(model => model.status === ModelStatusEnum.active),
+ isAPIKeySet: !!textGenerationModelList?.data?.some(model => model.status === ModelStatusEnum.active),
supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [],
plan,
isFetchedPlan,
diff --git a/web/context/workspace-context.tsx b/web/context/workspace-context.tsx
index 9350a959b4..da7dcf5a50 100644
--- a/web/context/workspace-context.tsx
+++ b/web/context/workspace-context.tsx
@@ -1,8 +1,7 @@
'use client'
import { createContext, useContext } from 'use-context-selector'
-import useSWR from 'swr'
-import { fetchWorkspaces } from '@/service/common'
+import { useWorkspaces } from '@/service/use-common'
import type { IWorkspace } from '@/models/common'
export type WorkspacesContextValue = {
@@ -20,7 +19,7 @@ type IWorkspaceProviderProps = {
export const WorkspaceProvider = ({
children,
}: IWorkspaceProviderProps) => {
- const { data } = useSWR({ url: '/workspaces' }, fetchWorkspaces)
+ const { data } = useWorkspaces()
return (
@@ -58,12 +55,7 @@ export const useCheckNotion = () => {
const type = searchParams.get('type')
const notionCode = searchParams.get('code')
const notionError = searchParams.get('error')
- const { data } = useSWR(
- (canBinding && notionCode)
- ? `/oauth/data-source/binding/notion?code=${notionCode}`
- : null,
- fetchDataSourceNotionBinding,
- )
+ const { data } = useNotionBinding(notionCode, canBinding)
useEffect(() => {
if (data)
diff --git a/web/service/common.ts b/web/service/common.ts
index 7a092a6a24..1793675bc5 100644
--- a/web/service/common.ts
+++ b/web/service/common.ts
@@ -1,4 +1,3 @@
-import type { Fetcher } from 'swr'
import { del, get, patch, post, put } from './base'
import type {
AccountIntegrate,
@@ -49,145 +48,145 @@ type LoginFail = {
message: string
}
type LoginResponse = LoginSuccess | LoginFail
-export const login: Fetcher }> = ({ url, body }) => {
- return post(url, { body }) as Promise
+export const login = ({ url, body }: { url: string; body: Record }): Promise => {
+ return post(url, { body })
}
-export const webAppLogin: Fetcher }> = ({ url, body }) => {
- return post(url, { body }, { isPublicAPI: true }) as Promise
+export const webAppLogin = ({ url, body }: { url: string; body: Record }): Promise => {
+ return post(url, { body }, { isPublicAPI: true })
}
-export const setup: Fetcher }> = ({ body }) => {
+export const setup = ({ body }: { body: Record }): Promise => {
return post('/setup', { body })
}
-export const initValidate: Fetcher }> = ({ body }) => {
+export const initValidate = ({ body }: { body: Record }): Promise => {
return post('/init', { body })
}
-export const fetchInitValidateStatus = () => {
+export const fetchInitValidateStatus = (): Promise => {
return get('/init')
}
-export const fetchSetupStatus = () => {
+export const fetchSetupStatus = (): Promise => {
return get('/setup')
}
-export const fetchUserProfile: Fetcher }> = ({ url, params }) => {
+export const fetchUserProfile = ({ url, params }: { url: string; params: Record }): Promise => {
return get(url, params, { needAllResponseContent: true })
}
-export const updateUserProfile: Fetcher }> = ({ url, body }) => {
+export const updateUserProfile = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const fetchLangGeniusVersion: Fetcher }> = ({ url, params }) => {
+export const fetchLangGeniusVersion = ({ url, params }: { url: string; params: Record }): Promise => {
return get(url, { params })
}
-export const oauth: Fetcher }> = ({ url, params }) => {
+export const oauth = ({ url, params }: { url: string; params: Record }): Promise => {
return get(url, { params })
}
-export const oneMoreStep: Fetcher }> = ({ url, body }) => {
+export const oneMoreStep = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const fetchMembers: Fetcher<{ accounts: Member[] | null }, { url: string; params: Record }> = ({ url, params }) => {
+export const fetchMembers = ({ url, params }: { url: string; params: Record }): Promise<{ accounts: Member[] | null }> => {
return get<{ accounts: Member[] | null }>(url, { params })
}
-export const fetchProviders: Fetcher }> = ({ url, params }) => {
+export const fetchProviders = ({ url, params }: { url: string; params: Record }): Promise => {
return get(url, { params })
}
-export const validateProviderKey: Fetcher = ({ url, body }) => {
+export const validateProviderKey = ({ url, body }: { url: string; body: { token: string } }): Promise => {
return post(url, { body })
}
-export const updateProviderAIKey: Fetcher = ({ url, body }) => {
+export const updateProviderAIKey = ({ url, body }: { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }): Promise => {
return post(url, { body })
}
-export const fetchAccountIntegrates: Fetcher<{ data: AccountIntegrate[] | null }, { url: string; params: Record }> = ({ url, params }) => {
+export const fetchAccountIntegrates = ({ url, params }: { url: string; params: Record }): Promise<{ data: AccountIntegrate[] | null }> => {
return get<{ data: AccountIntegrate[] | null }>(url, { params })
}
-export const inviteMember: Fetcher }> = ({ url, body }) => {
+export const inviteMember = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const updateMemberRole: Fetcher }> = ({ url, body }) => {
+export const updateMemberRole = ({ url, body }: { url: string; body: Record }): Promise => {
return put(url, { body })
}
-export const deleteMemberOrCancelInvitation: Fetcher = ({ url }) => {
+export const deleteMemberOrCancelInvitation = ({ url }: { url: string }): Promise => {
return del(url)
}
-export const sendOwnerEmail = (body: { language?: string }) =>
+export const sendOwnerEmail = (body: { language?: string }): Promise =>
post('/workspaces/current/members/send-owner-transfer-confirm-email', { body })
-export const verifyOwnerEmail = (body: { code: string; token: string }) =>
+export const verifyOwnerEmail = (body: { code: string; token: string }): Promise =>
post('/workspaces/current/members/owner-transfer-check', { body })
-export const ownershipTransfer = (memberID: string, body: { token: string }) =>
+export const ownershipTransfer = (memberID: string, body: { token: string }): Promise =>
post(`/workspaces/current/members/${memberID}/owner-transfer`, { body })
-export const fetchFilePreview: Fetcher<{ content: string }, { fileID: string }> = ({ fileID }) => {
+export const fetchFilePreview = ({ fileID }: { fileID: string }): Promise<{ content: string }> => {
return get<{ content: string }>(`/files/${fileID}/preview`)
}
-export const fetchCurrentWorkspace: Fetcher }> = ({ url, params }) => {
+export const fetchCurrentWorkspace = ({ url, params }: { url: string; params: Record }): Promise => {
return post(url, { body: params })
}
-export const updateCurrentWorkspace: Fetcher }> = ({ url, body }) => {
+export const updateCurrentWorkspace = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const fetchWorkspaces: Fetcher<{ workspaces: IWorkspace[] }, { url: string; params: Record }> = ({ url, params }) => {
+export const fetchWorkspaces = ({ url, params }: { url: string; params: Record }): Promise<{ workspaces: IWorkspace[] }> => {
return get<{ workspaces: IWorkspace[] }>(url, { params })
}
-export const switchWorkspace: Fetcher }> = ({ url, body }) => {
+export const switchWorkspace = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const updateWorkspaceInfo: Fetcher }> = ({ url, body }) => {
+export const updateWorkspaceInfo = ({ url, body }: { url: string; body: Record }): Promise => {
return post(url, { body })
}
-export const fetchDataSource: Fetcher<{ data: DataSourceNotion[] }, { url: string }> = ({ url }) => {
+export const fetchDataSource = ({ url }: { url: string }): Promise<{ data: DataSourceNotion[] }> => {
return get<{ data: DataSourceNotion[] }>(url)
}
-export const syncDataSourceNotion: Fetcher = ({ url }) => {
+export const syncDataSourceNotion = ({ url }: { url: string }): Promise => {
return get(url)
}
-export const updateDataSourceNotionAction: Fetcher = ({ url }) => {
+export const updateDataSourceNotionAction = ({ url }: { url: string }): Promise => {
return patch(url)
}
-export const fetchPluginProviders: Fetcher = (url) => {
+export const fetchPluginProviders = (url: string): Promise => {
return get(url)
}
-export const validatePluginProviderKey: Fetcher = ({ url, body }) => {
+export const validatePluginProviderKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise => {
return post(url, { body })
}
-export const updatePluginProviderAIKey: Fetcher = ({ url, body }) => {
+export const updatePluginProviderAIKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise => {
return post(url, { body })
}
-export const invitationCheck: Fetcher = ({ url, params }) => {
+export const invitationCheck = ({ url, params }: { url: string; params: { workspace_id?: string; email?: string; token: string } }): Promise => {
return get(url, { params })
}
-export const activateMember: Fetcher = ({ url, body }) => {
+export const activateMember = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const fetchModelProviders: Fetcher<{ data: ModelProvider[] }, string> = (url) => {
+export const fetchModelProviders = (url: string): Promise<{ data: ModelProvider[] }> => {
return get<{ data: ModelProvider[] }>(url)
}
@@ -195,197 +194,197 @@ export type ModelProviderCredentials = {
credentials?: Record
load_balancing: ModelLoadBalancingConfig
}
-export const fetchModelProviderCredentials: Fetcher = (url) => {
+export const fetchModelProviderCredentials = (url: string): Promise => {
return get(url)
}
-export const fetchModelLoadBalancingConfig: Fetcher<{
+export const fetchModelLoadBalancingConfig = (url: string): Promise<{
credentials?: Record
load_balancing: ModelLoadBalancingConfig
-}, string> = (url) => {
+}> => {
return get<{
credentials?: Record
load_balancing: ModelLoadBalancingConfig
}>(url)
}
-export const fetchModelProviderModelList: Fetcher<{ data: ModelItem[] }, string> = (url) => {
+export const fetchModelProviderModelList = (url: string): Promise<{ data: ModelItem[] }> => {
return get<{ data: ModelItem[] }>(url)
}
-export const fetchModelList: Fetcher<{ data: Model[] }, string> = (url) => {
+export const fetchModelList = (url: string): Promise<{ data: Model[] }> => {
return get<{ data: Model[] }>(url)
}
-export const validateModelProvider: Fetcher = ({ url, body }) => {
+export const validateModelProvider = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const validateModelLoadBalancingCredentials: Fetcher = ({ url, body }) => {
+export const validateModelLoadBalancingCredentials = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const setModelProvider: Fetcher = ({ url, body }) => {
+export const setModelProvider = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const deleteModelProvider: Fetcher = ({ url, body }) => {
+export const deleteModelProvider = ({ url, body }: { url: string; body?: any }): Promise => {
return del(url, { body })
}
-export const changeModelProviderPriority: Fetcher = ({ url, body }) => {
+export const changeModelProviderPriority = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const setModelProviderModel: Fetcher = ({ url, body }) => {
+export const setModelProviderModel = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const deleteModelProviderModel: Fetcher = ({ url }) => {
+export const deleteModelProviderModel = ({ url }: { url: string }): Promise => {
return del(url)
}
-export const getPayUrl: Fetcher<{ url: string }, string> = (url) => {
+export const getPayUrl = (url: string): Promise<{ url: string }> => {
return get<{ url: string }>(url)
}
-export const fetchDefaultModal: Fetcher<{ data: DefaultModelResponse }, string> = (url) => {
+export const fetchDefaultModal = (url: string): Promise<{ data: DefaultModelResponse }> => {
return get<{ data: DefaultModelResponse }>(url)
}
-export const updateDefaultModel: Fetcher = ({ url, body }) => {
+export const updateDefaultModel = ({ url, body }: { url: string; body: any }): Promise => {
return post(url, { body })
}
-export const fetchModelParameterRules: Fetcher<{ data: ModelParameterRule[] }, string> = (url) => {
+export const fetchModelParameterRules = (url: string): Promise<{ data: ModelParameterRule[] }> => {
return get<{ data: ModelParameterRule[] }>(url)
}
-export const fetchFileUploadConfig: Fetcher = ({ url }) => {
+export const fetchFileUploadConfig = ({ url }: { url: string }): Promise => {
return get(url)
}
-export const fetchNotionConnection: Fetcher<{ data: string }, string> = (url) => {
- return get(url) as Promise<{ data: string }>
+export const fetchNotionConnection = (url: string): Promise<{ data: string }> => {
+ return get<{ data: string }>(url)
}
-export const fetchDataSourceNotionBinding: Fetcher<{ result: string }, string> = (url) => {
- return get(url) as Promise<{ result: string }>
+export const fetchDataSourceNotionBinding = (url: string): Promise<{ result: string }> => {
+ return get<{ result: string }>(url)
}
-export const fetchApiBasedExtensionList: Fetcher = (url) => {
- return get(url) as Promise
+export const fetchApiBasedExtensionList = (url: string): Promise => {
+ return get(url)
}
-export const fetchApiBasedExtensionDetail: Fetcher = (url) => {
- return get(url) as Promise
+export const fetchApiBasedExtensionDetail = (url: string): Promise => {
+ return get(url)
}
-export const addApiBasedExtension: Fetcher = ({ url, body }) => {
- return post(url, { body }) as Promise
+export const addApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise => {
+ return post(url, { body })
}
-export const updateApiBasedExtension: Fetcher = ({ url, body }) => {
- return post(url, { body }) as Promise
+export const updateApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise => {
+ return post(url, { body })
}
-export const deleteApiBasedExtension: Fetcher<{ result: string }, string> = (url) => {
- return del(url) as Promise<{ result: string }>
+export const deleteApiBasedExtension = (url: string): Promise<{ result: string }> => {
+ return del<{ result: string }>(url)
}
-export const fetchCodeBasedExtensionList: Fetcher = (url) => {
- return get(url) as Promise
+export const fetchCodeBasedExtensionList = (url: string): Promise => {
+ return get(url)
}
-export const moderate = (url: string, body: { app_id: string; text: string }) => {
- return post(url, { body }) as Promise
+export const moderate = (url: string, body: { app_id: string; text: string }): Promise => {
+ return post(url, { body })
}
type RetrievalMethodsRes = {
retrieval_method: RETRIEVE_METHOD[]
}
-export const fetchSupportRetrievalMethods: Fetcher = (url) => {
+export const fetchSupportRetrievalMethods = (url: string): Promise => {
return get(url)
}
-export const getSystemFeatures = () => {
+export const getSystemFeatures = (): Promise => {
return get('/system-features')
}
-export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
+export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise =>
patch(url, { body })
-export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
+export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise =>
patch(url, { body })
-export const sendForgotPasswordEmail: Fetcher = ({ url, body }) =>
+export const sendForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise =>
post(url, { body })
-export const verifyForgotPasswordToken: Fetcher = ({ url, body }) => {
- return post(url, { body }) as Promise
+export const verifyForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise => {
+ return post(url, { body })
}
-export const changePasswordWithToken: Fetcher = ({ url, body }) =>
+export const changePasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise =>
post(url, { body })
-export const sendWebAppForgotPasswordEmail: Fetcher = ({ url, body }) =>
+export const sendWebAppForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise =>
post(url, { body }, { isPublicAPI: true })
-export const verifyWebAppForgotPasswordToken: Fetcher = ({ url, body }) => {
- return post(url, { body }, { isPublicAPI: true }) as Promise
+export const verifyWebAppForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise => {
+ return post(url, { body }, { isPublicAPI: true })
}
-export const changeWebAppPasswordWithToken: Fetcher = ({ url, body }) =>
+export const changeWebAppPasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise =>
post(url, { body }, { isPublicAPI: true })
-export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean) => {
+export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean): Promise<{ id: string; name: string; size: number; mime_type: string; url: string }> => {
return post<{ id: string; name: string; size: number; mime_type: string; url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent })
}
-export const sendEMailLoginCode = (email: string, language = 'en-US') =>
+export const sendEMailLoginCode = (email: string, language = 'en-US'): Promise =>
post('/email-code-login', { body: { email, language } })
-export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }) =>
+export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }): Promise =>
post('/email-code-login/validity', { body: data })
-export const sendResetPasswordCode = (email: string, language = 'en-US') =>
+export const sendResetPasswordCode = (email: string, language = 'en-US'): Promise =>
post('/forgot-password', { body: { email, language } })
-export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }) =>
+export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }): Promise =>
post('/forgot-password/validity', { body })
-export const sendWebAppEMailLoginCode = (email: string, language = 'en-US') =>
+export const sendWebAppEMailLoginCode = (email: string, language = 'en-US'): Promise =>
post('/email-code-login', { body: { email, language } }, { isPublicAPI: true })
-export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }) =>
+export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }): Promise =>
post('/email-code-login/validity', { body: data }, { isPublicAPI: true })
-export const sendWebAppResetPasswordCode = (email: string, language = 'en-US') =>
+export const sendWebAppResetPasswordCode = (email: string, language = 'en-US'): Promise =>
post('/forgot-password', { body: { email, language } }, { isPublicAPI: true })
-export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }) =>
+export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }): Promise =>
post('/forgot-password/validity', { body }, { isPublicAPI: true })
-export const sendDeleteAccountCode = () =>
+export const sendDeleteAccountCode = (): Promise