Forráskód Böngészése

feat: permission and security fixes (#5266)

Charles Zhou 10 hónapja
szülő
commit
cc4a4ec796

+ 15 - 0
api/controllers/console/app/app.py

@@ -129,6 +129,10 @@ class AppApi(Resource):
     @marshal_with(app_detail_fields_with_site)
     def put(self, app_model):
         """Update app"""
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('name', type=str, required=True, nullable=False, location='json')
         parser.add_argument('description', type=str, location='json')
@@ -147,6 +151,7 @@ class AppApi(Resource):
     @get_app_model
     def delete(self, app_model):
         """Delete app"""
+        # The role of the current user in the ta table must be admin, owner, or editor
         if not current_user.is_editor:
             raise Forbidden()
 
@@ -203,6 +208,10 @@ class AppNameApi(Resource):
     @get_app_model
     @marshal_with(app_detail_fields)
     def post(self, app_model):
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('name', type=str, required=True, location='json')
         args = parser.parse_args()
@@ -220,6 +229,10 @@ class AppIconApi(Resource):
     @get_app_model
     @marshal_with(app_detail_fields)
     def post(self, app_model):
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('icon', type=str, location='json')
         parser.add_argument('icon_background', type=str, location='json')
@@ -241,6 +254,7 @@ class AppSiteStatus(Resource):
         # The role of the current user in the ta table must be admin, owner, or editor
         if not current_user.is_editor:
             raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('enable_site', type=bool, required=True, location='json')
         args = parser.parse_args()
@@ -261,6 +275,7 @@ class AppApiStatus(Resource):
         # The role of the current user in the ta table must be admin or owner
         if not current_user.is_admin_or_owner:
             raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('enable_api', type=bool, required=True, location='json')
         args = parser.parse_args()

+ 53 - 1
api/controllers/console/app/workflow.py

@@ -3,7 +3,7 @@ import logging
 
 from flask import abort, request
 from flask_restful import Resource, marshal_with, reqparse
-from werkzeug.exceptions import InternalServerError, NotFound
+from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
 
 import services
 from controllers.console import api
@@ -36,6 +36,10 @@ class DraftWorkflowApi(Resource):
         """
         Get draft workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         # fetch draft workflow by app_model
         workflow_service = WorkflowService()
         workflow = workflow_service.get_draft_workflow(app_model=app_model)
@@ -54,6 +58,10 @@ class DraftWorkflowApi(Resource):
         """
         Sync draft workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         content_type = request.headers.get('Content-Type')
 
         if 'application/json' in content_type:
@@ -110,6 +118,10 @@ class AdvancedChatDraftWorkflowRunApi(Resource):
         """
         Run draft workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('inputs', type=dict, location='json')
         parser.add_argument('query', type=str, required=True, location='json', default='')
@@ -146,6 +158,10 @@ class AdvancedChatDraftRunIterationNodeApi(Resource):
         """
         Run draft workflow iteration node
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('inputs', type=dict, location='json')
         args = parser.parse_args()
@@ -179,6 +195,10 @@ class WorkflowDraftRunIterationNodeApi(Resource):
         """
         Run draft workflow iteration node
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('inputs', type=dict, location='json')
         args = parser.parse_args()
@@ -212,6 +232,10 @@ class DraftWorkflowRunApi(Resource):
         """
         Run draft workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('inputs', type=dict, required=True, nullable=False, location='json')
         parser.add_argument('files', type=list, required=False, location='json')
@@ -243,6 +267,10 @@ class WorkflowTaskStopApi(Resource):
         """
         Stop workflow task
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         AppQueueManager.set_stop_flag(task_id, InvokeFrom.DEBUGGER, current_user.id)
 
         return {
@@ -260,6 +288,10 @@ class DraftWorkflowNodeRunApi(Resource):
         """
         Run draft workflow node
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('inputs', type=dict, required=True, nullable=False, location='json')
         args = parser.parse_args()
@@ -286,6 +318,10 @@ class PublishedWorkflowApi(Resource):
         """
         Get published workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         # fetch published workflow by app_model
         workflow_service = WorkflowService()
         workflow = workflow_service.get_published_workflow(app_model=app_model)
@@ -301,6 +337,10 @@ class PublishedWorkflowApi(Resource):
         """
         Publish workflow
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         workflow_service = WorkflowService()
         workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user)
 
@@ -319,6 +359,10 @@ class DefaultBlockConfigsApi(Resource):
         """
         Get default block config
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         # Get default block configs
         workflow_service = WorkflowService()
         return workflow_service.get_default_block_configs()
@@ -333,6 +377,10 @@ class DefaultBlockConfigApi(Resource):
         """
         Get default block config
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         parser = reqparse.RequestParser()
         parser.add_argument('q', type=str, location='args')
         args = parser.parse_args()
@@ -363,6 +411,10 @@ class ConvertToWorkflowApi(Resource):
         Convert expert mode of chatbot app to workflow mode
         Convert Completion App to Workflow App
         """
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+        
         if request.data:
             parser = reqparse.RequestParser()
             parser.add_argument('name', type=str, required=False, nullable=True, location='json')

+ 12 - 6
api/controllers/console/auth/data_source_bearer_auth.py

@@ -16,15 +16,21 @@ class ApiKeyAuthDataSource(Resource):
     @login_required
     @account_initialization_required
     def get(self):
-        # The role of the current user in the table must be admin or owner
-        if not current_user.is_admin_or_owner:
-            raise Forbidden()
         data_source_api_key_bindings = ApiKeyAuthService.get_provider_auth_list(current_user.current_tenant_id)
         if data_source_api_key_bindings:
             return {
-                'settings': [data_source_api_key_binding.to_dict() for data_source_api_key_binding in
-                             data_source_api_key_bindings]}
-        return {'settings': []}
+                'sources': [{
+                                'id': data_source_api_key_binding.id,
+                                'category': data_source_api_key_binding.category,
+                                'provider': data_source_api_key_binding.provider,
+                                'disabled': data_source_api_key_binding.disabled,
+                                'created_at': int(data_source_api_key_binding.created_at.timestamp()),
+                                'updated_at': int(data_source_api_key_binding.updated_at.timestamp()),
+                            }
+                            for data_source_api_key_binding in
+                             data_source_api_key_bindings]
+            }
+        return {'sources': []}
 
 
 class ApiKeyAuthDataSourceBinding(Resource):

+ 19 - 19
web/app/(commonLayout)/apps/AppCard.tsx

@@ -279,27 +279,27 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
           'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
           tags.length ? 'flex' : '!hidden group-hover:!flex',
         )}>
-          <div className={cn('grow flex items-center gap-1 w-0')} onClick={(e) => {
-            e.stopPropagation()
-            e.preventDefault()
-          }}>
-            <div className={cn(
-              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
-              tags.length ? '!block' : '!hidden',
-            )}>
-              <TagSelector
-                position='bl'
-                type='app'
-                targetID={app.id}
-                value={tags.map(tag => tag.id)}
-                selectedTags={tags}
-                onCacheUpdate={setTags}
-                onChange={onRefresh}
-              />
-            </div>
-          </div>
           {isCurrentWorkspaceEditor && (
             <>
+              <div className={cn('grow flex items-center gap-1 w-0')} onClick={(e) => {
+                e.stopPropagation()
+                e.preventDefault()
+              }}>
+                <div className={cn(
+                  'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
+                  tags.length ? '!block' : '!hidden',
+                )}>
+                  <TagSelector
+                    position='bl'
+                    type='app'
+                    targetID={app.id}
+                    value={tags.map(tag => tag.id)}
+                    selectedTags={tags}
+                    onCacheUpdate={setTags}
+                    onChange={onRefresh}
+                  />
+                </div>
+              </div>
               <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200'/>
               <div className='!hidden group-hover:!flex shrink-0'>
                 <CustomPopover

+ 9 - 4
web/app/components/app-sidebar/app-info.tsx

@@ -16,7 +16,7 @@ import Divider from '@/app/components/base/divider'
 import Confirm from '@/app/components/base/confirm'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import { ToastContext } from '@/app/components/base/toast'
-import AppsContext from '@/context/app-context'
+import AppsContext, { useAppContext } from '@/context/app-context'
 import { useProviderContext } from '@/context/provider-context'
 import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps'
 import DuplicateAppModal from '@/app/components/app/duplicate-modal'
@@ -142,6 +142,8 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
     setShowConfirmDelete(false)
   }, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, t])
 
+  const { isCurrentWorkspaceEditor } = useAppContext()
+
   if (!appDetail)
     return null
 
@@ -154,10 +156,13 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
     >
       <div className='relative'>
         <PortalToFollowElemTrigger
-          onClick={() => setOpen(v => !v)}
+          onClick={() => {
+            if (isCurrentWorkspaceEditor)
+              setOpen(v => !v)
+          }}
           className='block'
         >
-          <div className={cn('flex cursor-pointer p-1 rounded-lg hover:bg-gray-100', open && 'bg-gray-100')}>
+          <div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}>
             <div className='relative shrink-0 mr-2'>
               <AppIcon size={expand ? 'large' : 'small'} icon={appDetail.icon} background={appDetail.icon_background} />
               <span className={cn(
@@ -185,7 +190,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
               <div className="grow w-0">
                 <div className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900'>
                   <div className='truncate' title={appDetail.name}>{appDetail.name}</div>
-                  <ChevronDown className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />
+                  {isCurrentWorkspaceEditor && <ChevronDown className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
                 </div>
                 <div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
                   {appDetail.mode === 'advanced-chat' && (

+ 5 - 5
web/app/components/datasets/create/website/index.tsx

@@ -5,8 +5,8 @@ import NoData from './no-data'
 import Firecrawl from './firecrawl'
 import { useModalContext } from '@/context/modal-context'
 import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
-import { fetchFirecrawlApiKey } from '@/service/datasets'
-import { type DataSourceWebsiteItem, WebsiteProvider } from '@/models/common'
+import { fetchDataSources } from '@/service/datasets'
+import { type DataSourceItem, DataSourceProvider } from '@/models/common'
 
 type Props = {
   onPreview: (payload: CrawlResultItem) => void
@@ -29,9 +29,9 @@ const Website: FC<Props> = ({
   const [isLoaded, setIsLoaded] = useState(false)
   const [isSetFirecrawlApiKey, setIsSetFirecrawlApiKey] = useState(false)
   const checkSetApiKey = useCallback(async () => {
-    const res = await fetchFirecrawlApiKey() as any
-    const list = res.settings.filter((item: DataSourceWebsiteItem) => item.provider === WebsiteProvider.fireCrawl && !item.disabled)
-    setIsSetFirecrawlApiKey(list.length > 0)
+    const res = await fetchDataSources() as any
+    const isFirecrawlSet = res.sources.some((item: DataSourceItem) => item.provider === DataSourceProvider.fireCrawl)
+    setIsSetFirecrawlApiKey(isFirecrawlSet)
   }, [])
 
   useEffect(() => {

+ 1 - 1
web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx

@@ -58,7 +58,7 @@ const DataSourceNotion: FC<Props> = ({
       type={DataSourceType.notion}
       isConfigured={connected}
       onConfigure={handleConnectNotion}
-      readonly={!isCurrentWorkspaceManager}
+      readOnly={!isCurrentWorkspaceManager}
       isSupportList
       configuredList={workspaces.map(workspace => ({
         id: workspace.id,

+ 2 - 2
web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx

@@ -11,7 +11,7 @@ import Button from '@/app/components/base/button'
 import type { FirecrawlConfig } from '@/models/common'
 import Field from '@/app/components/datasets/create/website/firecrawl/base/field'
 import Toast from '@/app/components/base/toast'
-import { createFirecrawlApiKey } from '@/service/datasets'
+import { createDataSourceApiKeyBinding } from '@/service/datasets'
 import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
 type Props = {
   onCancel: () => void
@@ -76,7 +76,7 @@ const ConfigFirecrawlModal: FC<Props> = ({
     }
     try {
       setIsSaving(true)
-      await createFirecrawlApiKey(postData)
+      await createDataSourceApiKeyBinding(postData)
       Toast.notify({
         type: 'success',
         message: t('common.api.success'),

+ 29 - 19
web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx

@@ -7,15 +7,15 @@ import cn from 'classnames'
 import Panel from '../panel'
 import { DataSourceType } from '../panel/types'
 import ConfigFirecrawlModal from './config-firecrawl-modal'
-import { fetchFirecrawlApiKey, removeFirecrawlApiKey } from '@/service/datasets'
+import { fetchDataSources, removeDataSourceApiKeyBinding } from '@/service/datasets'
 
 import type {
-  DataSourceWebsiteItem,
+  DataSourceItem,
 } from '@/models/common'
 import { useAppContext } from '@/context/app-context'
 
 import {
-  WebsiteProvider,
+  DataSourceProvider,
 } from '@/models/common'
 import Toast from '@/app/components/base/toast'
 
@@ -24,11 +24,11 @@ type Props = {}
 const DataSourceWebsite: FC<Props> = () => {
   const { t } = useTranslation()
   const { isCurrentWorkspaceManager } = useAppContext()
-  const [list, setList] = useState<DataSourceWebsiteItem[]>([])
+  const [sources, setSources] = useState<DataSourceItem[]>([])
   const checkSetApiKey = useCallback(async () => {
-    const res = await fetchFirecrawlApiKey() as any
-    const list = res.settings.filter((item: DataSourceWebsiteItem) => item.provider === WebsiteProvider.fireCrawl && !item.disabled)
-    setList(list)
+    const res = await fetchDataSources() as any
+    const list = res.sources
+    setSources(list)
   }, [])
 
   useEffect(() => {
@@ -46,23 +46,33 @@ const DataSourceWebsite: FC<Props> = () => {
     hideConfig()
   }, [checkSetApiKey, hideConfig])
 
-  const handleRemove = useCallback(async () => {
-    await removeFirecrawlApiKey(list[0].id)
-    setList([])
-    Toast.notify({
-      type: 'success',
-      message: t('common.api.remove'),
-    })
-  }, [list, t])
+  const getIdByProvider = (provider: string): string | undefined => {
+    const source = sources.find(item => item.provider === provider)
+    return source?.id
+  }
+
+  const handleRemove = useCallback((provider: string) => {
+    return async () => {
+      const dataSourceId = getIdByProvider(provider)
+      if (dataSourceId) {
+        await removeDataSourceApiKeyBinding(dataSourceId)
+        setSources(sources.filter(item => item.provider !== provider))
+        Toast.notify({
+          type: 'success',
+          message: t('common.api.remove'),
+        })
+      }
+    }
+  }, [sources, t])
 
   return (
     <>
       <Panel
         type={DataSourceType.website}
-        isConfigured={list.length > 0}
+        isConfigured={sources.length > 0}
         onConfigure={showConfig}
-        readonly={!isCurrentWorkspaceManager}
-        configuredList={list.map(item => ({
+        readOnly={!isCurrentWorkspaceManager}
+        configuredList={sources.map(item => ({
           id: item.id,
           logo: ({ className }: { className: string }) => (
             <div className={cn(className, 'flex items-center justify-center w-5 h-5 bg-white border border-gray-100 text-xs font-medium text-gray-500 rounded ml-3')}>🔥</div>
@@ -70,7 +80,7 @@ const DataSourceWebsite: FC<Props> = () => {
           name: 'FireCrawl',
           isActive: true,
         }))}
-        onRemove={handleRemove}
+        onRemove={handleRemove(DataSourceProvider.fireCrawl)}
       />
       {isShowConfig && (
         <ConfigFirecrawlModal onSaved={handleAdded} onCancel={hideConfig} />

+ 3 - 1
web/app/components/header/account-setting/data-source-page/panel/config-item.tsx

@@ -26,6 +26,7 @@ type Props = {
   notionActions?: {
     onChangeAuthorizedPage: () => void
   }
+  readOnly: boolean
 }
 
 const ConfigItem: FC<Props> = ({
@@ -33,6 +34,7 @@ const ConfigItem: FC<Props> = ({
   payload,
   onRemove,
   notionActions,
+  readOnly,
 }) => {
   const { t } = useTranslation()
   const isNotion = type === DataSourceType.notion
@@ -65,7 +67,7 @@ const ConfigItem: FC<Props> = ({
       )}
 
       {
-        isWebsite && (
+        isWebsite && !readOnly && (
           <div className='p-2 text-gray-500 cursor-pointer rounded-md hover:bg-black/5' onClick={onRemove} >
             <Trash03 className='w-4 h-4 ' />
           </div>

+ 28 - 28
web/app/components/header/account-setting/data-source-page/panel/index.tsx

@@ -14,7 +14,7 @@ type Props = {
   type: DataSourceType
   isConfigured: boolean
   onConfigure: () => void
-  readonly: boolean
+  readOnly: boolean
   isSupportList?: boolean
   configuredList: ConfigItemType[]
   onRemove: () => void
@@ -27,7 +27,7 @@ const Panel: FC<Props> = ({
   type,
   isConfigured,
   onConfigure,
-  readonly,
+  readOnly,
   configuredList,
   isSupportList,
   onRemove,
@@ -67,7 +67,7 @@ const Panel: FC<Props> = ({
                     className={
                       `flex items-center ml-3 px-3 h-7 bg-white border border-gray-200
                   rounded-md text-xs font-medium text-gray-700
-                  ${!readonly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
+                  ${!readOnly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
                     }
                     onClick={onConfigure}
                   >
@@ -79,7 +79,7 @@ const Panel: FC<Props> = ({
                     {isSupportList && <div
                       className={
                         `flex items-center px-3 py-1 min-h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
-                  ${!readonly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
+                  ${!readOnly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
                       }
                       onClick={onConfigure}
                     >
@@ -96,10 +96,10 @@ const Panel: FC<Props> = ({
           <div
             className={
               `flex items-center ml-3 px-3 h-7 bg-white border border-gray-200
-        rounded-md text-xs font-medium text-gray-700
-        ${!readonly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
+              rounded-md text-xs font-medium text-gray-700
+              ${!readOnly ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
             }
-            onClick={onConfigure}
+            onClick={!readOnly ? onConfigure : undefined}
           >
             {t('common.dataSource.configure')}
           </div>
@@ -108,28 +108,28 @@ const Panel: FC<Props> = ({
       </div>
       {
         isConfigured && (
-          <div className='flex items-center px-3 h-[18px]'>
-            <div className='text-xs font-medium text-gray-500'>
-              {isNotion ? t('common.dataSource.notion.connectedWorkspace') : t('common.dataSource.website.configuredCrawlers')}
+          <>
+            <div className='flex items-center px-3 h-[18px]'>
+              <div className='text-xs font-medium text-gray-500'>
+                {isNotion ? t('common.dataSource.notion.connectedWorkspace') : t('common.dataSource.website.configuredCrawlers')}
+              </div>
+              <div className='grow ml-3 border-t border-t-gray-100' />
             </div>
-            <div className='grow ml-3 border-t border-t-gray-100' />
-          </div>
-        )
-      }
-      {
-        isConfigured && (
-          <div className='px-3 pt-2 pb-3'>
-            {
-              configuredList.map(item => (
-                <ConfigItem
-                  key={item.id}
-                  type={type}
-                  payload={item}
-                  onRemove={onRemove}
-                  notionActions={notionActions} />
-              ))
-            }
-          </div>
+            <div className='px-3 pt-2 pb-3'>
+              {
+                configuredList.map(item => (
+                  <ConfigItem
+                    key={item.id}
+                    type={type}
+                    payload={item}
+                    onRemove={onRemove}
+                    notionActions={notionActions}
+                    readOnly={readOnly}
+                  />
+                ))
+              }
+            </div>
+          </>
         )
       }
     </div>

+ 7 - 15
web/models/common.ts

@@ -175,34 +175,26 @@ export type DataSourceNotion = {
 export enum DataSourceCategory {
   website = 'website',
 }
-export enum WebsiteProvider {
+export enum DataSourceProvider {
   fireCrawl = 'firecrawl',
 }
 
-export type WebsiteCredentials = {
-  auth_type: 'bearer'
-  config: {
-    base_url: string
-    api_key: string
-  }
-}
-
 export type FirecrawlConfig = {
   api_key: string
   base_url: string
 }
 
-export type DataSourceWebsiteItem = {
+export type DataSourceItem = {
   id: string
-  category: DataSourceCategory.website
-  provider: WebsiteProvider
-  credentials: WebsiteCredentials
+  category: DataSourceCategory
+  provider: DataSourceProvider
   disabled: boolean
   created_at: number
   updated_at: number
 }
-export type DataSourceWebsite = {
-  settings: DataSourceWebsiteItem[]
+
+export type DataSources = {
+  sources: DataSourceItem[]
 }
 
 export type GithubRepo = {

+ 3 - 3
web/service/datasets.ts

@@ -231,15 +231,15 @@ export const fetchDatasetApiBaseUrl: Fetcher<{ api_base_url: string }, string> =
   return get<{ api_base_url: string }>(url)
 }
 
-export const fetchFirecrawlApiKey = () => {
+export const fetchDataSources = () => {
   return get<CommonResponse>('api-key-auth/data-source')
 }
 
-export const createFirecrawlApiKey: Fetcher<CommonResponse, Record<string, any>> = (body) => {
+export const createDataSourceApiKeyBinding: Fetcher<CommonResponse, Record<string, any>> = (body) => {
   return post<CommonResponse>('api-key-auth/data-source/binding', { body })
 }
 
-export const removeFirecrawlApiKey: Fetcher<CommonResponse, string> = (id: string) => {
+export const removeDataSourceApiKeyBinding: Fetcher<CommonResponse, string> = (id: string) => {
   return del<CommonResponse>(`api-key-auth/data-source/${id}`)
 }