diff --git a/web/packages/axios/src/index.ts b/web/packages/axios/src/index.ts index 8d156a4b8..bc470c0cb 100644 --- a/web/packages/axios/src/index.ts +++ b/web/packages/axios/src/index.ts @@ -14,6 +14,8 @@ import type { ResponseType } from './type'; +axios.defaults.withCredentials = true; + function createCommonRequest( axiosConfig?: CreateAxiosDefaults, options?: Partial> diff --git a/web/packages/utils/src/storage.ts b/web/packages/utils/src/storage.ts index c17961c57..f1123bf89 100644 --- a/web/packages/utils/src/storage.ts +++ b/web/packages/utils/src/storage.ts @@ -3,8 +3,8 @@ import localforage from 'localforage'; /** The storage type */ export type StorageType = 'local' | 'session'; -export function createStorage(type: StorageType, storagePrefix: string) { - const stg = type === 'session' ? window.sessionStorage : window.localStorage; +export function createStorage(type: StorageType, storagePrefix: string, proxy?: any) { + const stg = proxy ? proxy : (type === 'session' ? window.sessionStorage : window.localStorage); const storage = { clear() { diff --git a/web/src/App.tsx b/web/src/App.tsx index 121043cd9..b528c6b35 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -4,7 +4,7 @@ import type { WatermarkProps } from 'antd'; import { info } from '@/constants/app'; import { router } from '@/router'; -import { getLocale } from '@/store/slice/app'; +import { changeLocale, getLocale } from '@/store/slice/app'; import { getDarkMode, getThemeSettings, themeColors } from '@/store/slice/theme'; import { getAntdTheme, setupThemeVarsToHtml, toggleCssDarkMode } from '@/store/slice/theme/shared'; import { localStg } from '@/utils/storage'; @@ -48,6 +48,13 @@ const App = () => { const locale = useAppSelector(getLocale); const { antdTheme, themeSettings } = useTheme(); + const dispatch = useAppDispatch(); + + useEffect(() => { + if (window.$wujie?.props?.locale) { + dispatch(changeLocale(window.$wujie?.props?.locale)); + } + }, [window.$wujie?.props?.locale]); return ( { {...watermarkProps} > } + fallback={} router={router} /> diff --git a/web/src/assets/svg-icon/folder.svg b/web/src/assets/svg-icon/folder.svg new file mode 100644 index 000000000..e1ba4ad18 --- /dev/null +++ b/web/src/assets/svg-icon/folder.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/web/src/assets/svg-icon/home.svg b/web/src/assets/svg-icon/home.svg new file mode 100644 index 000000000..deb948b6c --- /dev/null +++ b/web/src/assets/svg-icon/home.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/web/src/assets/svg-icon/puzzle.svg b/web/src/assets/svg-icon/puzzle.svg new file mode 100644 index 000000000..b8c80c408 --- /dev/null +++ b/web/src/assets/svg-icon/puzzle.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/web/src/assets/svg-icon/robot.svg b/web/src/assets/svg-icon/robot.svg new file mode 100644 index 000000000..da9463be4 --- /dev/null +++ b/web/src/assets/svg-icon/robot.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/web/src/assets/svg-icon/settings.svg b/web/src/assets/svg-icon/settings.svg new file mode 100644 index 000000000..b0dc48501 --- /dev/null +++ b/web/src/assets/svg-icon/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/components/common/icon.jsx b/web/src/components/common/icon.jsx index f0391ea6d..60652bebb 100644 --- a/web/src/components/common/icon.jsx +++ b/web/src/components/common/icon.jsx @@ -3,7 +3,10 @@ import React from 'react'; import FontIcon from '@/components/common/font_icon'; -const Icon = ({ className, src, style, ...rest }) => { +import normalizeUrl from 'normalize-url'; +import { getProxyEndpoint } from '@/components/micro/utils' + +const Icon = ({ className, src, style, server, ...rest }) => { if (!src) { return null; } @@ -17,9 +20,13 @@ const Icon = ({ className, src, style, ...rest }) => { /> ); } + let formatSrc = src + if (!src.startsWith('http')) { + formatSrc = normalizeUrl(`${getProxyEndpoint() || server}/${src}`) + } return ( { + renderRoot(root) + }; + window.__WUJIE_UNMOUNT = () => { + root.unmount() + }; + window.__WUJIE.mount() + + setupIcons(); + setupHistoryHooks(); +} + +function setupIcons() { + const parentDoc = window.$wujie?.props?.parentDocument; + if (!parentDoc) return; + + const existingMicroIcon = parentDoc.getElementById('__MICRO__SVG_ICON_LOCAL__'); + if (existingMicroIcon) return; + + const sourceIcon = document.getElementById('__SVG_ICON_LOCAL__'); + if (!sourceIcon) return; + + const clonedIcon = sourceIcon.cloneNode(true) as any; + clonedIcon.id = '__MICRO__SVG_ICON_LOCAL__'; + + parentDoc.body.appendChild(clonedIcon); +} + +function setupHistoryHooks() { + let lastUrl = window.location.href; + + function handleUrlChange() { + const currentUrl = window.location.href; + if (currentUrl !== lastUrl) { + lastUrl = currentUrl; + window.$wujie?.props?.onRouteChange({ + url: currentUrl, + }) + } + } + + const originalPushState = window.history.pushState; + window.history.pushState = function(...args) { + originalPushState.apply(this, args); + handleUrlChange(); + }; + + const originalReplaceState = window.history.replaceState; + window.history.replaceState = function(...args) { + originalReplaceState.apply(this, args); + handleUrlChange(); + }; +} \ No newline at end of file diff --git a/web/src/components/micro/utils.ts b/web/src/components/micro/utils.ts new file mode 100644 index 000000000..cce587b7d --- /dev/null +++ b/web/src/components/micro/utils.ts @@ -0,0 +1,12 @@ +export function getEndpoint() { + return window.__POWERED_BY_WUJIE__ && window.$wujie?.props?.endpoint ? window.$wujie?.props?.endpoint : window.location.origin; +} + +export function getProxyEndpoint() { + const proxy = window.__POWERED_BY_WUJIE__ ? window.$wujie?.props?.proxy_endpoint : '' + return proxy +} + +export function getName(name: string) { + return window.__POWERED_BY_WUJIE__ && window.$wujie?.props?.name ? window.$wujie?.props?.name : name; +} \ No newline at end of file diff --git a/web/src/components/stateless/common/GlobalLoading.tsx b/web/src/components/stateless/common/GlobalLoading.tsx index 5f2a77c7b..c8fabb88a 100644 --- a/web/src/components/stateless/common/GlobalLoading.tsx +++ b/web/src/components/stateless/common/GlobalLoading.tsx @@ -11,12 +11,12 @@ const loadingClasses = [ 'right-0 bottom-0 animate-delay-1500' ]; -const GlobalLoading = memo(() => { - const { t } = useTranslation(); +const GlobalLoading = memo((props: any) => { + const { className } = props; const darkMode = useAppSelector(getDarkMode); return ( -
+
{darkMode ? (
diff --git a/web/src/layouts/base-layout/BaseLayout.tsx b/web/src/layouts/base-layout/BaseLayout.tsx index b6d1992c3..0784c9510 100644 --- a/web/src/layouts/base-layout/BaseLayout.tsx +++ b/web/src/layouts/base-layout/BaseLayout.tsx @@ -20,6 +20,7 @@ import GlobalHeader from '../modules/global-header'; import GlobalMenu from '../modules/global-menu'; import GlobalSider from '../modules/global-sider'; import GlobalTab from '../modules/global-tab'; +import { logout } from '@/service/api'; const ThemeDrawer = lazy(() => import('../modules/theme-drawer')); @@ -36,6 +37,7 @@ const BaseLayout = () => { const fullContent = useAppSelector(getFullContent); const dispatch = useAppDispatch(); const responsive = useResponsive(); + const nav = useNavigate() const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = useMixMenuContext(); const contentXScrollable = useAppSelector(getContentXScrollable); @@ -98,7 +100,16 @@ const BaseLayout = () => { } }, [isMobile, dispatch]); - const isMicro = window.$wujie?.props?.isMicro; + const isMicro = window.__POWERED_BY_WUJIE__; + + useEffect(() => { + if (window.$wujie?.props?.onMicroMounted) { + window.$wujie?.props?.onMicroMounted({ + nav, + logout: async () => await logout({ ignoreError: true }) + }) + } + }, [isMicro]) return ( { ) } Sider={ - window.$wujie?.props?.isMicro ? null : ( + isMicro ? null : ( { ) } > - + = ({ children }) => { const locale = useAppSelector(getLocale); + const { t } = useTranslation(); + const update = useUpdate(); const activeFirstLevelMenuKey = useAppSelector(selectActiveFirstLevelMenuKey); @@ -69,6 +72,42 @@ const MenuProvider: FC = ({ children }) => { update(); }, [locale]); + useEffect(() => { + if (window.$wujie?.props?.onRoutesUpdate) { + const formatRoutes = (routes: any[] = [], newRoutes: any[] = [], pathPrefix?: string) => { + routes.forEach((item) => { + let activeRoute + if (item.meta?.activeMenu) { + activeRoute = routes.find((r) => r.name === item.meta?.activeMenu) + } + const route = { + ...item, + path: `${pathPrefix || ''}${item.path}`, + localeName: item.meta?.i18nKey ? t(item.meta?.i18nKey) : undefined, + icon: renderToString(), + hideInMenu: item.meta?.hideInMenu, + activeRoute: pathPrefix ? undefined : ( + activeRoute ? { + ...activeRoute, + path: `${activeRoute.path}`, + localeName: activeRoute.meta?.i18nKey ? t(activeRoute.meta?.i18nKey) : undefined, + icon: renderToString(), + hideInMenu: activeRoute.meta?.hideInMenu, + } : undefined + ) + } + newRoutes.push(route) + if (item.children) { + formatRoutes(item.children, newRoutes, `${route.path}/`) + } + }) + } + const newRoutes: any[] = [] + formatRoutes(sortRoutes, newRoutes) + window.$wujie?.props?.onRoutesUpdate(newRoutes) + } + }, [sortRoutes]) + const mixMenuContext = { activeFirstLevelMenuKey, allMenus: menus, diff --git a/web/src/main.tsx b/web/src/main.tsx index ef250779f..491e746a3 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -12,6 +12,19 @@ import './plugins/assets'; import { setupI18n } from './locales'; import { setupAppVersionNotification, setupDayjs, setupIconifyOffline, setupLoading, setupNProgress } from './plugins'; import './icons'; +import { setupMicro } from './components/micro/index.tsx'; + +function renderRoot(root) { + root.render( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + + + + + + ); +} function setupApp() { setupI18n(); @@ -25,21 +38,17 @@ function setupApp() { setupRouter(); setupDayjs(); - - setupAppVersionNotification(); - + const container = document.getElementById('root'); if (!container) return; const root = createRoot(container); - root.render( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - - - - - - ); + + if (window.__POWERED_BY_WUJIE__) { + setupMicro(root, renderRoot) + } else { + setupAppVersionNotification(); + renderRoot(root) + } } setupApp(); diff --git a/web/src/pages/ai-assistant/list/index.tsx b/web/src/pages/ai-assistant/list/index.tsx index fc420c8ae..c53835029 100644 --- a/web/src/pages/ai-assistant/list/index.tsx +++ b/web/src/pages/ai-assistant/list/index.tsx @@ -9,6 +9,7 @@ import useQueryParams from '@/hooks/common/queryParams'; import { cloneAssistant, deleteAssistant, searchAssistant, updateAssistant } from '@/service/api/assistant'; import { formatESSearchResult } from '@/service/request/es'; import { Api } from '@/types/api'; +import { getServer } from '@/store/slice/server'; type Assistant = Api.LLM.Assistant; @@ -22,6 +23,8 @@ export function Component() { const { hasAuth } = useAuth(); + const server = useAppSelector(getServer); + const permissions = { read: hasAuth('coco#assistant/read'), create: hasAuth('coco#assistant/create'), @@ -129,6 +132,7 @@ export function Component() { height='1em' src={record.icon} width='1em' + server={server} /> )} diff --git a/web/src/pages/ai-assistant/modules/ModelSelect.tsx b/web/src/pages/ai-assistant/modules/ModelSelect.tsx index b99473e99..0e0e7a4ac 100644 --- a/web/src/pages/ai-assistant/modules/ModelSelect.tsx +++ b/web/src/pages/ai-assistant/modules/ModelSelect.tsx @@ -4,6 +4,7 @@ import ModelSettings from './ModelSettings'; import InfiniIcon from '@/components/common/icon'; import { getLocale } from '@/store/slice/app'; import { Form, Input } from 'antd'; +import { getServer } from '@/store/slice/server'; const DefaultModelSettings = { temperature: 0.7, @@ -141,6 +142,7 @@ export default (props: any) => { }, [providers]); const locale = useAppSelector(getLocale); + const server = useAppSelector(getServer); const [sorter, setSorter] = useState([]); const [filters, setFilters] = useState({}); @@ -159,6 +161,7 @@ export default (props: any) => { height='1em' src={item.icon} width='1em' + server={server} /> )} diff --git a/web/src/pages/connector/edit/[id].tsx b/web/src/pages/connector/edit/[id].tsx index 865738c1d..ad65876e5 100644 --- a/web/src/pages/connector/edit/[id].tsx +++ b/web/src/pages/connector/edit/[id].tsx @@ -16,6 +16,7 @@ export function Component() { const { t } = useTranslation(); const nav = useNavigate(); const [form] = Form.useForm(); + const server = useAppSelector(getServer); const route = useRoute(); const connectorID = route.params.id @@ -190,6 +191,7 @@ export function Component() { height="2em" src={connector?.icon} width="2em" + server={server} /> ) : ( @@ -297,6 +299,7 @@ export function Component() { const AssetsIconsView = ({ value = {} }) => { const { t } = useTranslation(); + const server = useAppSelector(getServer); const icons = Object.keys(value).map(key => { return { icon: value[key], @@ -318,6 +321,7 @@ const AssetsIconsView = ({ value = {} }) => { height="1em" src={icon.icon} width="1em" + server={server} /> {icon.type} diff --git a/web/src/pages/data-source/detail/modules/FileManagement.tsx b/web/src/pages/data-source/detail/modules/FileManagement.tsx index 50e067f0b..d2c1f9133 100644 --- a/web/src/pages/data-source/detail/modules/FileManagement.tsx +++ b/web/src/pages/data-source/detail/modules/FileManagement.tsx @@ -15,6 +15,7 @@ import { } from '@/service/api'; import { formatESSearchResult } from '@/service/request/es'; import useQueryParams from '@/hooks/common/queryParams'; +import { getServer } from '@/store/slice/server'; interface DataType { category: string; @@ -37,6 +38,8 @@ const FileManagement = props => { const responsive = useResponsive(); const { t } = useTranslation(); + const server = useAppSelector(getServer); + const { addSharesToData, isEditorOwner, hasEdit, isResourceShare } = useResource(); const { hasAuth } = useAuth(); @@ -246,6 +249,7 @@ const FileManagement = props => { height='1em' src={imgSrc} width='1em' + server={server} /> ) : ( @@ -269,6 +273,7 @@ const FileManagement = props => { height='1em' src={imgSrc} width='1em' + server={server} /> ) : ( diff --git a/web/src/pages/data-source/new-first/index.tsx b/web/src/pages/data-source/new-first/index.tsx index 6d644a9a4..6995def99 100644 --- a/web/src/pages/data-source/new-first/index.tsx +++ b/web/src/pages/data-source/new-first/index.tsx @@ -10,6 +10,7 @@ import InfiniIcon from '@/components/common/icon'; import useQueryParams from '@/hooks/common/queryParams'; import { searchConnector } from '@/service/api/connector'; import { formatESSearchResult } from '@/service/request/es'; +import { getServer } from '@/store/slice/server'; const ConnectorCategory = { CloudStorage: 'cloud_storage', @@ -19,6 +20,7 @@ const ConnectorCategory = { export function Component() { const [queryParams, setQueryParams] = useQueryParams({ from: 0, size: 12 }); const { t } = useTranslation(); + const server = useAppSelector(getServer); const nav = useNavigate(); const onAddClick = (key: string) => { nav(`/data-source/new/?type=${key}`); @@ -118,6 +120,7 @@ export function Component() { height="2em" src={connector.icon} width="2em" + server={server} /> {connector.name} diff --git a/web/src/pages/home/index.tsx b/web/src/pages/home/index.tsx index 887ba31eb..7cf1433ab 100644 --- a/web/src/pages/home/index.tsx +++ b/web/src/pages/home/index.tsx @@ -177,10 +177,10 @@ export function Component() { ) : ( -
{data?.endpoint}
+
{data?.endpoint}
)} { - permissions.update && !managed && ( + permissions.update && !managed && !window.__POWERED_BY_WUJIE__ && (