Files
APIPark/frontend/packages/common/src/contexts/GlobalStateContext.tsx
T
2025-02-18 19:51:28 +08:00

547 lines
16 KiB
TypeScript

import { ErrorBoundary } from '@ant-design/pro-components'
import NotFound from '@common/components/aoplatform/NotFound'
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { PERMISSION_DEFINITION } from '@common/const/permissions'
import { RouteConfig } from '@common/const/type'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { MenuItem } from '@common/utils/navigation'
import { checkAccess } from '@common/utils/permission'
import { ProtectedRoute } from '@core/components/aoplatform/RenderRoutes'
import Login from '@core/pages/Login'
import Root from '@core/pages/Root'
import Playground from '@core/pages/playground'
import DataMaskingCompare from '@core/pages/policy/dataMasking/DataMaskingCompare'
import { App } from 'antd'
import { createContext, Dispatch, FC, ReactNode, useContext, useEffect, useReducer, useState } from 'react'
import { useLocaleContext } from './LocaleContext'
interface GlobalState {
isAuthenticated: boolean
userData: UserData | null
version: string
updateDate: string
powered: string
mainPage: string
language: string
pluginsLoaded: boolean
}
interface UserData {
username: string
}
export type GlobalAction =
| { type: 'LOGIN' }
| { type: 'LOGOUT' }
| { type: 'UPDATE_USERDATA'; userData: UserData }
| { type: 'UPDATE_VERSION'; version: string }
| { type: 'UPDATE_DATE'; updateDate: string }
| { type: 'UPDATE_POWER'; powered: string }
| { type: 'UPDATE_MAIN_PAGE'; mainPage: string }
| { type: 'UPDATE_LANGUAGE'; language: string }
| { type: 'SET_PLUGINS_LOADED'; pluginsLoaded: boolean }
const mockData = [
{
name: '工作空间',
key: 'workspace',
path: '/guide',
icon: 'ic:baseline-space-dashboard',
children: [
{
name: '首页',
key: 'guide',
path: '/guide',
icon: 'ic:baseline-home',
access: 'all'
},
{
name: '服务',
key: 'service',
path: '/service',
icon: 'ic:baseline-blinds-closed',
access: 'all'
},
{
name: '消费者',
key: 'consumer',
path: '/consumer',
icon: 'ic:baseline-apps',
access: 'all'
},
{
name: '团队',
key: 'team',
path: '/team',
icon: 'ic:baseline-people-alt',
access: 'all'
}
// {
// "name": "路由组件",
// "key": "router",
// "path": "/router1",
// "icon": "ic:baseline-people-alt",
// "access": "all"
// }
]
},
{
name: 'API 市场',
key: 'serviceHub',
path: '/serviceHub',
icon: 'ic:baseline-hub',
access: 'system.api_portal.api_portal.view'
},
{
name: '仪表盘',
key: 'analytics',
path: '/analytics',
icon: 'ic:baseline-bar-chart',
children: [
{
name: '运行视图',
key: 'analytics',
path: '/analytics',
icon: 'ic:baseline-bar-chart',
access: 'system.analysis.run_view.view'
}
],
access: 'system.analysis.run_view.view'
},
{
name: '系统设置',
key: 'operationCenter',
path: '/commonsetting',
icon: 'ic:baseline-settings',
children: [
{
name: '系统',
key: 'serviceHubSetting',
path: '/commonsetting',
children: [
{
name: '常规',
key: 'commonsetting',
path: '/commonsetting',
icon: 'ic:baseline-hub',
access: 'system.api_market.service_classification.view'
},
{
name: 'API 网关',
key: 'cluster',
path: '/cluster',
icon: 'ic:baseline-device-hub',
access: 'system.settings.api_gateway.view'
}
]
},
{
name: 'AI',
key: 'aiSettings',
path: '/aisetting',
children: [
{
name: 'AI 模型',
key: 'aiProviders',
path: '/aisetting',
icon: 'hugeicons:ai-network',
access: 'system.settings.ai_provider.view'
},
{
name: 'API Key 负载',
key: 'aiKeys',
path: '/keysetting',
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'
},
{
name: '模型灾备',
key: 'loadBalancing',
path: '/loadBalancing',
icon: 'ph:network-x',
access: 'system.settings.ai_balance.view'
}
]
},
{
name: '用户',
key: 'organization',
path: '/member',
children: [
{
name: '账号',
key: 'member',
path: '/member',
icon: 'ic:baseline-people-alt',
access: 'system.settings.account.view'
},
{
name: '角色',
key: 'role',
path: '/role',
icon: 'ic:baseline-verified-user',
access: 'system.organization.role.view'
}
]
},
{
name: '集成',
key: 'maintenanceCenter',
path: '/datasourcing',
children: [
{
name: '数据源',
key: 'datasourcing',
path: '/datasourcing',
icon: 'ic:baseline-monitor-heart',
access: 'system.settings.data_source.view'
},
{
name: '全局策略',
key: 'globalpolicy',
path: '/globalpolicy',
icon: 'icon-park-solid:exchange-three',
access: 'system.settings.data_source.view'
},
{
name: '证书',
key: 'cert',
path: '/cert',
icon: 'ic:baseline-security',
access: 'system.settings.ssl_certificate.view'
},
{
name: '日志',
key: 'logsettings',
path: '/logsettings',
icon: 'ic:baseline-sticky-note-2',
access: 'system.settings.log_configuration.view'
}
]
}
]
}
]
/*
存储用户登录、信息、权限等数据
*/
export const GlobalContext = createContext<
| {
state: GlobalState
dispatch: Dispatch<GlobalAction>
accessData: Map<string, string[]>
pluginAccessDictionary: { [k: string]: string }
menuList: MenuItem[]
getGlobalAccessData: () => Promise<{ access: string[] }>
getTeamAccessData: (teamId: string) => void
getPluginAccessDictionary: (pluginData: { [k: string]: string }) => void
getMenuList: () => void
resetAccess: () => void
cleanTeamAccessData: () => void
checkPermission: (
access: keyof (typeof PERMISSION_DEFINITION)[0] | Array<keyof (typeof PERMISSION_DEFINITION)[0]>
) => boolean
teamDataFlushed: boolean
accessInit: boolean
aiConfigFlushed: boolean
setAiConfigFlushed: (flush: boolean) => void
routeConfig: RouteConfig[]
setRouterConfig: (isRoot: boolean, config: RouteConfig) => void
addRouteConfig: (parentRoute: RouteConfig, config: RouteConfig) => void
fetchData: ReturnType<typeof useFetch>['fetchData']
$t: typeof $t
}
| undefined
>(undefined)
const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState => {
switch (action.type) {
case 'LOGIN':
return {
...state,
isAuthenticated: true
}
case 'LOGOUT':
return {
...state,
isAuthenticated: false,
userData: null
}
case 'UPDATE_USERDATA':
return {
...state,
userData: action.userData
}
case 'UPDATE_VERSION':
return {
...state,
version: action.version
}
case 'UPDATE_DATE':
return {
...state,
updateDate: action.updateDate
}
case 'UPDATE_POWER':
return {
...state,
powered: action.powered
}
case 'UPDATE_MAIN_PAGE':
return {
...state,
mainPage: action.mainPage
}
case 'UPDATE_LANGUAGE':
return {
...state,
language: action.language
}
case 'SET_PLUGINS_LOADED':
return {
...state,
pluginsLoaded: action.pluginsLoaded
}
default:
return state
}
}
export const DefaultRouteConfig = [
{ path: '/', pathMatch: 'full', component: <Root />, key: 'root' },
{ path: '/login', component: <Login />, key: 'login' },
{ path: '/dataMaskCompare/:logId/:serviceId?/:teamId?', component: <DataMaskingCompare />, key: 'dataMaskCompare' },
{
path: '/',
pathMatch: 'prefix',
component: <ProtectedRoute />,
key: 'basciLayout',
children: [
{
path: '*',
component: (
<ErrorBoundary>
<NotFound />
</ErrorBoundary>
),
key: 'errorBoundary'
}
]
},
{ path: '/playground', component: <Playground />, key: 'playground' }
]
// Create a context provider component
export const GlobalProvider: FC<{ children: ReactNode }> = ({ children }) => {
const { message } = App.useApp()
const { setLocale } = useLocaleContext()
const [state, dispatch] = useReducer(globalReducer, {
isAuthenticated: false, //mock用
userData: null,
version: '1.0.0',
updateDate: '2024-07-01',
powered: 'Powered by https://apipark.com',
mainPage: '/guide/page',
language: sessionStorage.getItem('i18nextLng') || 'en-US',
pluginsLoaded: false
})
const [accessData, setAccessData] = useState<Map<string, string[]>>(new Map())
const [pluginAccessDictionary, setPluginAccessDictionary] = useState<{ [k: string]: string }>({})
const [teamDataFlushed, setTeamDataFlushed] = useState<boolean>(false)
const [accessInit, setAccessInit] = useState<boolean>(false)
const [aiConfigFlushed, setAiConfigFlushed] = useState<boolean>(false)
let getGlobalAccessPromise: Promise<BasicResponse<{ access: string[] }>> | null = null
const [menuList, setMenuList] = useState<MenuItem[]>(mockData)
const [routeConfig, setRouteConfigState] = useState<RouteConfig[]>(DefaultRouteConfig)
useEffect(() => {
setLocale(state.language)
}, [state.language, setLocale])
const { fetchData } = useFetch()
const setRouterConfig = (isRoot: boolean, config: RouteConfig) => {
setRouteConfigState((prevConfig) => {
if (isRoot) {
return [config, ...prevConfig]
} else {
const rootRoute = prevConfig.find((route) => route.path === '/' && route?.pathMatch === 'prefix')
if (rootRoute) {
rootRoute.children = rootRoute.children ? [config, ...rootRoute.children] : [config]
}
return [...prevConfig]
}
})
}
const addRouteConfig = (parentRoute: RouteConfig, config: RouteConfig) => {
const addConfigToParent = (routes: RouteConfig[]): RouteConfig[] => {
return routes.map((route) => {
if (route.key === parentRoute.key) {
route.children = route.children ? [...route.children, config] : [config]
} else if (route.children) {
route.children = addConfigToParent(route.children)
}
return route
})
}
setRouteConfigState((prevConfig) => addConfigToParent(prevConfig))
}
const getGlobalAccessData = () => {
if (getGlobalAccessPromise) {
return getGlobalAccessPromise
}
getGlobalAccessPromise = new Promise((resolve, reject) =>
fetchData<BasicResponse<{ access: string[] }>>('profile/permission/system', { method: 'GET' }).then(
(response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setAccessInit(true)
setAccessData((prevData) => new Map(prevData).set('system', data.access))
resolve(data.response)
getGlobalAccessPromise = null
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
reject(data.msg || $t(RESPONSE_TIPS.error))
}
}
)
)
return getGlobalAccessData
}
const getTeamAccessData = (teamId: string) => {
fetchData<BasicResponse<{ access: string[] }>>('profile/permission/team', {
method: 'GET',
eoParams: { team: teamId }
}).then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setAccessData((prevData) => new Map(prevData).set('team', data.access))
setTeamDataFlushed(true)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
}
const getMenuList = () => {
// TODO 等待对接后端接口
// fetchData<BasicResponse<{ access:string[]}>>('profile/permission/team',{method:'GET',eoParams:{team:teamId}},).then(response=>{
// const {code,data,msg} = response
// if(code === STATUS_CODE.SUCCESS){
// setMenuList(data.menus)
// }else{
// message.error(msg || $t(RESPONSE_TIPS.error))
// }
// })
}
const cleanTeamAccessData = () => {
setTeamDataFlushed(false)
setAccessData((prevData) => prevData.set('team', []))
}
const getPluginAccessDictionary = (pluginData: { [k: string]: string }) => {
setPluginAccessDictionary(pluginData)
}
const resetAccess = () => {
setAccessData(new Map())
setAccessInit(false)
setPluginAccessDictionary({})
}
const checkPermission = (
access: keyof (typeof PERMISSION_DEFINITION)[0] | Array<keyof (typeof PERMISSION_DEFINITION)[0]>
) => {
let revs = false
if (Array.isArray(access)) {
revs = access.some((item) => checkAccess(item, accessData))
} else {
revs = checkAccess(access, accessData)
}
return revs
}
return (
<GlobalContext.Provider
value={{
state,
dispatch,
accessData,
pluginAccessDictionary,
getGlobalAccessData,
getPluginAccessDictionary,
getTeamAccessData,
teamDataFlushed,
getMenuList,
menuList,
cleanTeamAccessData,
resetAccess,
checkPermission,
accessInit,
aiConfigFlushed,
setAiConfigFlushed,
routeConfig,
setRouterConfig,
addRouteConfig,
fetchData,
$t: $t
}}
>
{children}
</GlobalContext.Provider>
)
}
// Create a custom hook for accessing the global context
export const useGlobalContext = () => {
const context = useContext(GlobalContext)
if (!context) {
console.warn('useGlobalContext must be used within a GlobalProvider. Returning default context.')
return {
state: {
isAuthenticated: false,
userData: null,
version: '1.0.0',
updateDate: '',
powered: '',
mainPage: '',
language: sessionStorage.getItem('i18nextLng') || 'en-US',
pluginsLoaded: false
},
dispatch: () => {},
accessData: new Map(),
pluginAccessDictionary: {},
menuList: [],
getGlobalAccessData: async () => ({ access: [] }),
getTeamAccessData: () => {},
getPluginAccessDictionary: () => {},
getMenuList: () => {},
resetAccess: () => {},
cleanTeamAccessData: () => {},
checkPermission: () => false,
teamDataFlushed: false,
accessInit: false,
aiConfigFlushed: false,
setAiConfigFlushed: () => {},
routeConfig: [],
setRouterConfig: () => {},
addRouteConfig: () => {},
fetchData: async () => ({}),
$t: (key: string) => key
}
}
return context
}