diff --git a/frontend/packages/common/src/const/permissions.ts b/frontend/packages/common/src/const/permissions.ts index ed4ac03b..dbe2786e 100644 --- a/frontend/packages/common/src/const/permissions.ts +++ b/frontend/packages/common/src/const/permissions.ts @@ -674,10 +674,21 @@ export const PERMISSION_DEFINITION = [ anyOf: [{ backend: ['project.permission_manager'] }] } }, + 'system.settings.ai_key_resource.view': { + granted: { + anyOf: [{ backend: ['system.settings.ai_key_resource.view'] }] + } + }, 'system.settings.ai_key_resource.manager': { granted: { anyOf: [{ backend: ['system.settings.ai_key_resource.manager'] }] } + }, + + 'system.settings.ai_api.view': { + granted: { + anyOf: [{ backend: ['system.settings.ai_api.view'] }] + } } } ] diff --git a/frontend/packages/common/src/contexts/GlobalStateContext.tsx b/frontend/packages/common/src/contexts/GlobalStateContext.tsx index d7720cf3..da730fa6 100644 --- a/frontend/packages/common/src/contexts/GlobalStateContext.tsx +++ b/frontend/packages/common/src/contexts/GlobalStateContext.tsx @@ -151,15 +151,15 @@ const mockData = [ name: 'APIKey 资源池', key: 'aiKeys', path: '/keysetting', - icon: 'ic:baseline-key' - // access: 'system.settings.ai_key_resource.view' + icon: 'ic:baseline-key', + access: 'system.settings.ai_key_resource.view' }, { name: 'AI API', key: 'aiApiList', path: '/aiApis', - icon: 'ic:baseline-api' - // access: 'system.settings.ai_api.view' + icon: 'ic:baseline-api', + access: 'system.settings.ai_api.view' } ] }, diff --git a/frontend/packages/core/src/const/const.tsx b/frontend/packages/core/src/const/const.tsx index f4833725..79b6452d 100644 --- a/frontend/packages/core/src/const/const.tsx +++ b/frontend/packages/core/src/const/const.tsx @@ -478,17 +478,22 @@ export const routerMap: Map = new Map([ { path: 'list', key: 'apiList', - lazy: lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiApis/index.tsx')), + lazy: lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiApis/index.tsx')) }, { - path: 'service/:teamId/aiInside/:serviceId/route/:routeId/:type', - key: 'apiDetail', - lazy: lazy( - () => - import( - /* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate.tsx' + path: 'service/:teamId/aiInside/:serviceId', + key: 'aiApisServiceInside', + lazy: lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx')), + children: [ + { + path: 'route/:routeId/:type', + key: 'aiApisServiceInsideRouteDetail', + lazy: lazy( + () => + import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate') ) - ) + } + ] } ] } diff --git a/frontend/packages/core/src/pages/aiApis/index.tsx b/frontend/packages/core/src/pages/aiApis/index.tsx index 4e5d975d..c5cadf06 100644 --- a/frontend/packages/core/src/pages/aiApis/index.tsx +++ b/frontend/packages/core/src/pages/aiApis/index.tsx @@ -39,12 +39,14 @@ const ApiSettings: React.FC = () => { const handlePreview = (record: APIs) => { navigate(`../service/${record.team.id}/aiInside/${record.service.id}/route/${record.id}/apiDetail`) } - const requestApis = async (params: any & { - pageSize: number; - current: number; - }, + const requestApis = async ( + params: any & { + pageSize: number + current: number + }, sort: Record, - filter: Record) => { + filter: Record + ) => { if (!selectedProvider) return setQueryBtnLoading(true) try { @@ -75,16 +77,22 @@ const ApiSettings: React.FC = () => { setTotal(response.data.total) const modalMap: { [key: string]: string - } = response.data?.condition?.models.reduce((acc: { [key: string]: string }, item: { id: string; name: string }) => { - acc[item.id] = $t(item.name) - return acc - }, {}) + } = response.data?.condition?.models.reduce( + (acc: { [key: string]: string }, item: { id: string; name: string }) => { + acc[item.id] = $t(item.name) + return acc + }, + {} + ) const serviceMap: { [key: string]: string - } = response.data?.condition?.services.reduce((acc: { [key: string]: string }, item: { id: string; name: string }) => { - acc[item.id] = $t(item.name) - return acc - }, {}) + } = response.data?.condition?.services.reduce( + (acc: { [key: string]: string }, item: { id: string; name: string }) => { + acc[item.id] = $t(item.name) + return acc + }, + {} + ) setTableColumns(modalMap, serviceMap) return { data: response.data.apis || [], @@ -107,11 +115,14 @@ const ApiSettings: React.FC = () => { } } } - const setTableColumns = (modalMap: { - [key: string]: string - }, serviceMap: { - [key: string]: string - }) => { + const setTableColumns = ( + modalMap: { + [key: string]: string + }, + serviceMap: { + [key: string]: string + } + ) => { setColumns([ { title: $t('AI 服务'), @@ -287,15 +298,18 @@ const ApiSettings: React.FC = () => { } - request={async (params: any & { - pageSize: number; - current: number; - }, + request={async ( + params: any & { + pageSize: number + current: number + }, sort: Record, - filter: Record) => requestApis(params, sort, filter)} + filter: Record + ) => requestApis(params, sort, filter)} onSearchWordChange={(e) => { setSearchWord(e.target.value) }} + onRowClick={(row: APIs) => handlePreview(row)} showPagination={true} searchPlaceholder={$t('请输入 APIURL 搜索')} columns={columns} diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx index 3459f146..16178602 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx @@ -1,166 +1,262 @@ +import InsidePage from '@common/components/aoplatform/InsidePage.tsx' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const.tsx' +import { PERMISSION_DEFINITION } from '@common/const/permissions.ts' +import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx' +import { useFetch } from '@common/hooks/http.ts' +import { $t } from '@common/locales/index.ts' +import { getItem } from '@common/utils/navigation.tsx' +import { RouterParams } from '@core/components/aoplatform/RenderRoutes.tsx' +import { AiServiceConfigFieldType } from '@core/const/ai-service/type.ts' +import { App, Menu, MenuProps } from 'antd' +import { ItemType, MenuItemGroupType, MenuItemType } from 'antd/es/menu/interface' +import Paragraph from 'antd/es/typography/Paragraph' +import { cloneDeep } from 'lodash-es' +import { FC, useEffect, useMemo, useState } from 'react' +import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom' +import { useAiServiceContext } from '../../contexts/AiServiceContext.tsx' +const APP_MODE = import.meta.env.VITE_APP_MODE -import {FC, useEffect, useMemo, useState} from "react"; -import {Link, Outlet, useLocation, useNavigate, useParams} from "react-router-dom"; -import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx"; -import {App, Menu, MenuProps} from "antd"; -import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx"; -import {useFetch} from "@common/hooks/http.ts"; -import { useAiServiceContext} from "../../contexts/AiServiceContext.tsx"; -import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx"; -import { PERMISSION_DEFINITION } from "@common/const/permissions.ts"; -import InsidePage from "@common/components/aoplatform/InsidePage.tsx"; -import Paragraph from "antd/es/typography/Paragraph"; -import { cloneDeep } from "lodash-es"; -import { $t } from "@common/locales/index.ts"; -import { getItem } from "@common/utils/navigation.tsx"; -import { AiServiceConfigFieldType } from "@core/const/ai-service/type.ts"; -import { MenuItemGroupType, MenuItemType, ItemType } from "antd/es/menu/interface"; -const APP_MODE = import.meta.env.VITE_APP_MODE; +const AiServiceInsidePage: FC = () => { + const { message } = App.useApp() + const { teamId, serviceId, apiId, routeId, policyId } = useParams() + const location = useLocation() + const currentUrl = location.pathname + const { fetchData } = useFetch() + const { setPrefixForce, setApiPrefix, aiServiceInfo, setAiServiceInfo } = useAiServiceContext() + const { accessData, checkPermission, accessInit, state } = useGlobalContext() + const [activeMenu, setActiveMenu] = useState() + const navigateTo = useNavigate() + const [showMenu, setShowMenu] = useState(false) -const AiServiceInsidePage:FC = ()=> { - const { message } = App.useApp() - const { teamId,serviceId,apiId, routeId,policyId } = useParams(); - const location = useLocation() - const currentUrl = location.pathname - const {fetchData} = useFetch() - const { setPrefixForce,setApiPrefix ,aiServiceInfo ,setAiServiceInfo} = useAiServiceContext() - const { accessData,checkPermission,accessInit,state} = useGlobalContext() - const [activeMenu, setActiveMenu] = useState() - const navigateTo = useNavigate() - const [showMenu, setShowMenu] = useState(false) + const getAiServiceInfo = () => { + fetchData>('service/info', { + method: 'GET', + eoParams: { team: teamId, service: serviceId } + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + setAiServiceInfo(data.service) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } - const getAiServiceInfo = ()=>{ - fetchData>('service/info',{method:'GET',eoParams:{team:teamId, service:serviceId}}).then(response=>{ - const {code,data,msg} = response - if(code === STATUS_CODE.SUCCESS){ - setAiServiceInfo(data.service) - }else{ - message.error(msg || $t(RESPONSE_TIPS.error)) - } - }) - } + const getApiDefine = () => { + setApiPrefix('') + setPrefixForce(false) + fetchData>('service/router/define', { + method: 'GET', + eoParams: { service: serviceId, team: teamId } + }).then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + setApiPrefix(data.prefix) + setPrefixForce(data.force) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + } - - const getApiDefine = ()=>{ - setApiPrefix('') - setPrefixForce(false) - fetchData>('service/router/define',{method:'GET',eoParams:{service:serviceId,team:teamId}}).then(response=>{ - const {code,data,msg} = response - if(code === STATUS_CODE.SUCCESS){ - setApiPrefix(data.prefix) - setPrefixForce(data.force) - }else{ - message.error(msg || $t(RESPONSE_TIPS.error)) - } - }) - } - - - - const SYSTEM_PAGE_MENU_ITEMS = useMemo(()=>[ - getItem($t('服务'), 'assets', null, + const SYSTEM_PAGE_MENU_ITEMS = useMemo( + () => [ + getItem( + $t('服务'), + 'assets', + null, [ - getItem({$t('API 路由')}, 'route',undefined,undefined,undefined,'team.service.router.view'), - getItem({$t('API 文档')}, 'api',undefined,undefined,undefined,'team.service.api_doc.view'), - getItem({$t('使用说明')}, 'document',undefined,undefined,undefined,'team.service.service_intro.view'), - getItem({$t('服务策略')}, 'servicepolicy', undefined, undefined, undefined, 'team.service.policy.view'), - getItem({$t('发布')}, 'publish',undefined,undefined,undefined,'team.service.release.view'), - ], - 'group'), - getItem($t('订阅管理'), 'provideSer', null, - [ - getItem({$t('订阅审核')}, 'approval',undefined,undefined,undefined,'team.service.subscription.view'), - getItem({$t('订阅方管理')}, 'subscriber',undefined,undefined,undefined,'team.service.subscription.view'), + getItem( + {$t('API 路由')}, + 'route', + undefined, + undefined, + undefined, + 'team.service.router.view' + ), + getItem( + {$t('API 文档')}, + 'api', + undefined, + undefined, + undefined, + 'team.service.api_doc.view' + ), + getItem( + {$t('使用说明')}, + 'document', + undefined, + undefined, + undefined, + 'team.service.service_intro.view' + ), + getItem( + {$t('服务策略')}, + 'servicepolicy', + undefined, + undefined, + undefined, + 'team.service.policy.view' + ), + getItem( + {$t('发布')}, + 'publish', + undefined, + undefined, + undefined, + 'team.service.release.view' + ) ], - 'group'), - getItem($t('管理'), 'mng', null, + 'group' + ), + getItem( + $t('订阅管理'), + 'provideSer', + null, [ - APP_MODE === 'pro' ? getItem({$t('调用拓扑图')}, 'topology',undefined,undefined,undefined,'project.myAiService.topology.view'):null, - getItem({$t('设置')}, 'setting',undefined,undefined,undefined,'')], - 'group'), -],[state.language]) + getItem( + {$t('订阅审核')}, + 'approval', + undefined, + undefined, + undefined, + 'team.service.subscription.view' + ), + getItem( + {$t('订阅方管理')}, + 'subscriber', + undefined, + undefined, + undefined, + 'team.service.subscription.view' + ) + ], + 'group' + ), + getItem( + $t('管理'), + 'mng', + null, + [ + APP_MODE === 'pro' + ? getItem( + {$t('调用拓扑图')}, + 'topology', + undefined, + undefined, + undefined, + 'project.myAiService.topology.view' + ) + : null, + getItem({$t('设置')}, 'setting', undefined, undefined, undefined, '') + ], + 'group' + ) + ], + [state.language] + ) - - const menuData = useMemo(()=>{ - const filterMenu = (menu:MenuItemGroupType[])=>{ - const newMenu = cloneDeep(menu) - return newMenu!.filter((m:MenuItemGroupType )=>{ - if(m&&m.children && m.children.length > 0){ - m.children = m.children.filter( - (c)=>{ - if(!c) return false - return (((c as MenuItemType&{access:string} ).access ? - checkPermission((c as MenuItemType&{access:string} ).access as keyof typeof PERMISSION_DEFINITION[0]): - true))}) - } - return m.children && m.children.length > 0 - }) + const menuData = useMemo(() => { + const filterMenu = (menu: MenuItemGroupType[]) => { + const newMenu = cloneDeep(menu) + return newMenu!.filter((m: MenuItemGroupType) => { + if (m && m.children && m.children.length > 0) { + m.children = m.children.filter((c) => { + if (!c) return false + return (c as MenuItemType & { access: string }).access + ? checkPermission( + (c as MenuItemType & { access: string }).access as keyof (typeof PERMISSION_DEFINITION)[0] + ) + : true + }) } - const filteredMenu = filterMenu(SYSTEM_PAGE_MENU_ITEMS as MenuItemGroupType[]) - const menu = activeMenu ?? filteredMenu[0]?.children ? filteredMenu[0]?.children?.[0]?.key : filteredMenu[0]?.key - if(menu && currentUrl.split('/')[-1] !== menu){ - navigateTo(`/service/${teamId}/aiInside/${serviceId}/${menu}`) - } - return filteredMenu || [] - },[accessData,accessInit, SYSTEM_PAGE_MENU_ITEMS]) - - const onMenuClick: MenuProps['onClick'] = ({key}) => { - setActiveMenu(key) - }; - - useEffect(() => { - // route edit and policy edit page don't need to show menu - setShowMenu(!routeId && !currentUrl.includes('route/create') && !policyId &&!currentUrl.includes('servicepolicy/datamasking/create')) + return m.children && m.children.length > 0 + }) + } + const filteredMenu = filterMenu(SYSTEM_PAGE_MENU_ITEMS as MenuItemGroupType[]) + const menu = (activeMenu ?? filteredMenu[0]?.children) ? filteredMenu[0]?.children?.[0]?.key : filteredMenu[0]?.key + if (menu && currentUrl.split('/')[-1] !== menu) { + navigateTo(`/service/${teamId}/aiInside/${serviceId}/${menu}`) + } + return filteredMenu || [] + }, [accessData, accessInit, SYSTEM_PAGE_MENU_ITEMS]) - if(apiId !== undefined){ - setActiveMenu('api') - } else if(currentUrl.includes('servicepolicy')){ - setActiveMenu('servicepolicy') - } else if(serviceId !== currentUrl.split('/')[currentUrl.split('/').length - 1]){ - setActiveMenu(currentUrl.split('/')[currentUrl.split('/').length - 1]) - }else{ - setActiveMenu('route') - } - }, [currentUrl]); + const onMenuClick: MenuProps['onClick'] = ({ key }) => { + setActiveMenu(key) + } - useEffect(()=>{ - if(accessData && checkPermission('team.service.router.view')){ - getApiDefine() - } - },[accessData]) - - useEffect(()=>{ - if( activeMenu && serviceId === currentUrl.split('/')[currentUrl.split('/').length - 1]){ - navigateTo(`/service/${teamId}/aiInside/${serviceId}/${activeMenu}`) - } - },[activeMenu]) - - useEffect(() => { - serviceId && getAiServiceInfo() - }, [serviceId]); - - return ( - <>{showMenu ? - {$t('服务 ID')}:{serviceId || '-'} - }]} - backUrl="/service/list"> -
- [] } - /> -
- -
-
-
: } - + useEffect(() => { + // route edit and policy edit page don't need to show menu + setShowMenu( + !routeId && + !currentUrl.includes('route/create') && + !policyId && + !currentUrl.includes('servicepolicy/datamasking/create') ) + + if (apiId !== undefined) { + setActiveMenu('api') + } else if (currentUrl.includes('servicepolicy')) { + setActiveMenu('servicepolicy') + } else if (serviceId !== currentUrl.split('/')[currentUrl.split('/').length - 1]) { + setActiveMenu(currentUrl.split('/')[currentUrl.split('/').length - 1]) + } else { + setActiveMenu('route') + } + }, [currentUrl]) + + useEffect(() => { + if (accessData && checkPermission('team.service.router.view')) { + getApiDefine() + } + }, [accessData]) + + useEffect(() => { + if (activeMenu && serviceId === currentUrl.split('/')[currentUrl.split('/').length - 1]) { + navigateTo(`/service/${teamId}/aiInside/${serviceId}/${activeMenu}`) + } + }, [activeMenu]) + + useEffect(() => { + serviceId && getAiServiceInfo() + }, [serviceId]) + + return ( + <> + {showMenu ? ( + + {$t('服务 ID')}:{serviceId || '-'} + + ) + } + ]} + backUrl="/service/list" + > +
+ []} + /> +
+ +
+
+
+ ) : ( + + )} + + ) } -export default AiServiceInsidePage \ No newline at end of file +export default AiServiceInsidePage diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx index 4e44dd5a..489ed981 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx @@ -104,8 +104,8 @@ const AiServiceInsideRouterCreate = () => { }) .catch((errInfo) => Promise.reject(errInfo)) } - const isDelete = type === 'apiDetail' - const backUrl = isDelete ? `/aiApis/list` : `/service/${teamId}/aiInside/${serviceId}/route` + const isAIApiPreview = type === 'apiDetail' + const backUrl = isAIApiPreview ? `/aiApis/list` : `/service/${teamId}/aiInside/${serviceId}/route` const openDrawer = (type: 'edit') => { setDrawerType(type) } @@ -210,7 +210,7 @@ const AiServiceInsideRouterCreate = () => { }, []) const addVariable = () => { - if (isDelete) return + if (isAIApiPreview) return form.setFieldsValue({ variables: [...form.getFieldValue('variables'), { key: '', value: '', require: true }] }) @@ -273,7 +273,7 @@ const AiServiceInsideRouterCreate = () => { - {type !== 'apiDetail' && ( + {!isAIApiPreview && ( @@ -298,7 +298,7 @@ const AiServiceInsideRouterCreate = () => { spinning={loading} wrapperClassName=" pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X" > - +
{ label={$t('提示词')} name="prompt"> @@ -361,7 +361,7 @@ const AiServiceInsideRouterCreate = () => {
{$t('变量')}