diff --git a/web/config/config.js b/web/config/config.js index 22a7be05c..230a77fb4 100644 --- a/web/config/config.js +++ b/web/config/config.js @@ -5,25 +5,69 @@ import webpackPlugin from "./plugin.config"; import defaultSettings from "../src/defaultSettings"; import packageJson from "../package.json"; -const ProxyTarget = "http://localhost:9001" +const ProxyTarget = "https://localhost:9000"; + +// 统一代理路径列表 +const proxyPaths = [ + "/elasticsearch/", + "/_search-center/", + "/gateway/", + "/_info", + "/config/", + "/environments/", + "/pipeline/", + "/queue/", + "/task/", + "/tasks/", + "/debug/", + "/alerting/", + "/health", + "/stats", + "/keystore", + "/user", + "/role/", + "/permission/", + "/account/", + "/notification/", + "/agent/", + "/insight/", + "/host/", + "/_platform/", + "/migration/", + "/comparison/", + "/_license/", + "/setup/", + "/layout", + "/credential", + "/setting/", + "/email/", + "/data/", + "/instance", + "/collection/", +]; + +// 生成代理配置,统一加上 secure: false +const proxy = proxyPaths.reduce(function(acc, path) { + acc[path] = { + target: ProxyTarget, + changeOrigin: true, + secure: false, // 忽略自签名证书 + }; + return acc; +}, {}); export default { - // add for transfer to umi plugins: [ [ "umi-plugin-react", { antd: true, - dva: { - hmr: true, - }, - targets: { - ie: 11, - }, + dva: { hmr: true }, + targets: { ie: 11 }, locale: { - enable: true, // default false - default: "en-US", // default zh-CN - baseNavigator: true, // default true, when it is true, will use `navigator.language` overwrite default + enable: true, + default: "en-US", + baseNavigator: true, }, dynamicImport: { loadingComponent: "./components/PageLoading/index", @@ -39,17 +83,8 @@ export default { : {}), }, ], - // [ - // 'umi-plugin-ga', - // { - // code: 'UA-12123-6', - // judge: () => process.env.APP_TYPE === 'site', - // }, - // ], ], - targets: { - ie: 11, - }, + targets: { ie: 11 }, define: { APP_TYPE: process.env.APP_TYPE || "", ENV: process.env.NODE_ENV, @@ -59,170 +94,16 @@ export default { APP_AUTHOR: packageJson.author, APP_OFFICIAL_WEBSITE: packageJson.official_website || "", }, - // 路由配置 routes: pageRoutes, - // Theme for antd - // https://ant.design/docs/react/customize-theme-cn - theme: { - "primary-color": defaultSettings.primaryColor, - }, - externals: { - // '@antv/data-set': 'DataSet', - }, - proxy: { - "/elasticsearch/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/_search-center/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/static/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/gateway/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/_info": { - target: ProxyTarget, - changeOrigin: true, - }, - "/config/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/environments/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/pipeline/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/queue/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/task/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/tasks/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/debug/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/alerting/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/health": { - target: ProxyTarget, - changeOrigin: true, - }, - "/stats": { - target: ProxyTarget, - changeOrigin: true, - }, - "/keystore": { - target: ProxyTarget, - changeOrigin: true, - }, - "/user": { - target: ProxyTarget, - changeOrigin: true, - }, - "/role/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/permission/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/account/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/notification/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/agent/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/insight/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/host/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/_platform/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/migration/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/comparison/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/_license/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/setup/": { - target: ProxyTarget, - changeOrigin: true, - }, - "/layout": { - target: ProxyTarget, - changeOrigin: true, - }, - "/credential": { - target: ProxyTarget, - changeOrigin: true, - }, - "/setting": { - target: ProxyTarget, - changeOrigin: true, - }, - "/email": { - target: ProxyTarget, - changeOrigin: true, - }, - "/data": { - target: ProxyTarget, - changeOrigin: true, - }, - "/instance": { - target: ProxyTarget, - changeOrigin: true, - }, - "/collection": { - target: ProxyTarget, - changeOrigin: true, - }, - }, + theme: { "primary-color": defaultSettings.primaryColor }, + proxy: proxy, ignoreMomentLocale: true, - lessLoaderOptions: { - javascriptEnabled: true, - }, + ...(process.env.NODE_ENV === "production" ? { devtool: false } : {}), + lessLoaderOptions: { javascriptEnabled: true }, disableRedirectHoist: true, cssLoaderOptions: { modules: true, - getLocalIdent: (context, localIdentName, localName) => { + getLocalIdent: function(context, localIdentName, localName) { if ( context.resourcePath.includes("node_modules") || context.resourcePath.includes("ant.design.pro.less") || @@ -236,33 +117,19 @@ export default { const antdProPath = match[1].replace(".less", ""); const arr = antdProPath .split("/") - .map((a) => a.replace(/([A-Z])/g, "-$1")) - .map((a) => a.toLowerCase()); + .map(function(a) { return a.replace(/([A-Z])/g, "-$1"); }) + .map(function(a) { return a.toLowerCase(); }); return `antd-pro${arr.join("-")}-${localName}`.replace(/--/g, "-"); } return localName; }, }, - - // chainWebpack: webpackPlugin, - cssnano: { - mergeRules: false, - }, - - // extra configuration for egg + cssnano: { mergeRules: false }, runtimePublicPath: true, hash: true, outputPath: "../.public", - manifest: { - fileName: "../.public/manifest.json", - publicPath: "", - }, - + manifest: { fileName: "../.public/manifest.json", publicPath: "" }, copy: ["./src/assets/favicon.ico"], history: "hash", - // exportStatic: { - // // htmlSuffix: true, - // dynamicRoot: true, - // }, sass: {}, }; diff --git a/web/config/router.config.js b/web/config/router.config.js index b65fc9896..65f8994e3 100644 --- a/web/config/router.config.js +++ b/web/config/router.config.js @@ -1,3 +1,13 @@ +import fs from 'fs'; +import path from 'path'; + +const pluginRoutes = []; + +const pluginRoutePath = path.resolve('config','router.enterprise.js'); +if (fs.existsSync(pluginRoutePath)) { + pluginRoutes.push(...require(pluginRoutePath).default); +} + export default [ // user { @@ -155,7 +165,7 @@ export default [ }, ], }, - + ...pluginRoutes, // alerting { path: "/alerting", @@ -277,6 +287,11 @@ export default [ "agent.instance:read", ], routes: [ + { + path: "/resource/runtime", + hideInMenu: true, + hideInBreadcrumb: true, + }, { path: "/resource/runtime/instance/new", name: "runtime.new_instance", @@ -381,6 +396,8 @@ export default [ name: "system", icon: "setting", authority: [ + "system.cluster:all", + "system.cluster:read", "system.credential:all", "system.credential:read", "system.security:all", @@ -391,11 +408,27 @@ export default [ "system.smtp_server:read" ], routes: [ + { + path: "/system/settings", + name: "settings", + component: "./System/Settings/index", + authority: [ + "system.cluster:all", + "system.cluster:read", + "system.smtp_server:all", + "system.smtp_server:read", + ], + }, { path: "/system/email_server", - name: "smtp_server", - component: "./System/Email/Server", - authority: ["system.smtp_server:all", "system.smtp_server:read"], + component: "./System/Settings/index", + hideInMenu: true, + authority: [ + "system.cluster:all", + "system.cluster:read", + "system.smtp_server:all", + "system.smtp_server:read", + ], }, { path: "/system/credential", diff --git a/web/mock/api.js b/web/mock/api.js index b20bb393f..a45f60f4d 100644 --- a/web/mock/api.js +++ b/web/mock/api.js @@ -374,22 +374,6 @@ export default { "eol_date": "2023-12-31T10:10:10Z" }, "tagline": "A light-weight but powerful agent." - }, - "basic_auth": {}, - "endpoint": "http://192.168.3.25:2900", - "host": { - "name": "INFINI-4.local", - "os": { - "name": "darwin", - "architecture": "arm64", - "version": "22.6.0" - } - }, - "network": { - "ip": [ - "192.168.3.25" - ], - "major_ip": "192.168.3.25" } }); }, diff --git a/web/mock/rbac/admin.js b/web/mock/rbac/admin.js index 4194be7da..398b96fca 100644 --- a/web/mock/rbac/admin.js +++ b/web/mock/rbac/admin.js @@ -101,18 +101,18 @@ export default { "indices.get_mapping", "indices.upgrade", "indices.validate_query", - "indices.exists_template", + "template.exists", "indices.get_upgrade", "indices.update_aliases", "indices.analyze", "indices.exists", "indices.close", - "indices.delete_template", + "template.delete", "indices.get_field_mapping", "indices.delete_alias", "indices.exists_type", - "indices.get_template", - "indices.put_template", + "template.get", + "template.put", "indices.refresh", "indices.segments", "indices.termvectors", diff --git a/web/src/app.js b/web/src/app.js index 79a046140..cb9efd9f2 100644 --- a/web/src/app.js +++ b/web/src/app.js @@ -21,12 +21,30 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { getAuthEnabled } from "./utils/authority"; +import { + getAuthEnabled, + getEnterpriseTaskManagerEnabled, + refreshApplicationSettings, +} from "./utils/authority"; +import { startActivityAwareTokenRefresh } from "./utils/auth_session"; import request from "./utils/request"; import { setSetupRequired } from "@/utils/setup"; import { getHealth } from "@/services/system" import PlatformContainer from "./components/PlatformContainer"; import React from "react"; +import { message, notification } from "antd"; + +message.config({ + maxCount: 3, +}); + +notification.config({ + placement: "topRight", +}); + +const CHUNK_RELOAD_KEY = "console.chunk-reload"; +const CHUNK_RELOAD_WINDOW_MS = 15000; +const CHUNK_RELOAD_QUERY_KEY = "_reload_ts"; if (!String.prototype.replaceAll) { String.prototype.replaceAll = function(search, replacement) { @@ -35,17 +53,176 @@ if (!String.prototype.replaceAll) { }; } +const isChunkLoadError = (error) => { + const reason = + error?.message || + error?.reason?.message || + error?.reason || + ""; + return /ChunkLoadError|Loading chunk [\d]+ failed|CSS_CHUNK_LOAD_FAILED/i.test( + `${reason}` + ); +}; + +const isInjectedScriptConnectionError = (error) => { + const reason = + error?.message || + error?.reason?.message || + error?.reason || + error || + ""; + const stack = `${error?.stack || error?.reason?.stack || ""}`; + return ( + `${reason}`.includes("Could not establish connection. Receiving end does not exist.") && + (stack.includes("single-file-bootstrap.bundle.js") || stack === "") + ); +}; + +const suppressUnhandledRejection = (event) => { + event.preventDefault?.(); + event.stopImmediatePropagation?.(); + event.stopPropagation?.(); +}; + +const getCanonicalLocation = () => { + const url = new URL(window.location.href); + url.searchParams.delete(CHUNK_RELOAD_QUERY_KEY); + return `${url.pathname}${url.search}${url.hash}`; +}; + +const buildReloadLocation = () => { + const url = new URL(window.location.href); + url.searchParams.set(CHUNK_RELOAD_QUERY_KEY, `${Date.now()}`); + return `${url.pathname}${url.search}${url.hash}`; +}; + +const shouldReloadForChunkError = () => { + const now = Date.now(); + const currentLocation = getCanonicalLocation(); + + try { + const raw = sessionStorage.getItem(CHUNK_RELOAD_KEY); + const previous = raw ? JSON.parse(raw) : null; + if ( + previous?.location === currentLocation && + now - Number(previous?.timestamp || 0) < CHUNK_RELOAD_WINDOW_MS + ) { + return false; + } + sessionStorage.setItem( + CHUNK_RELOAD_KEY, + JSON.stringify({ + location: currentLocation, + timestamp: now, + }) + ); + } catch (e) {} + + return true; +}; + +const installChunkReloadRecovery = () => { + if (typeof window === "undefined" || window.__consoleChunkReloadRecoveryInstalled) { + return; + } + + window.__consoleChunkReloadRecoveryInstalled = true; + + const handleChunkFailure = (error) => { + if (!isChunkLoadError(error) || !shouldReloadForChunkError()) { + return false; + } + window.location.replace(buildReloadLocation()); + return true; + }; + + window.addEventListener("error", (event) => { + handleChunkFailure(event?.error || new Error(event?.message || "")); + }); + + window.addEventListener("unhandledrejection", (event) => { + if (handleChunkFailure(event?.reason)) { + suppressUnhandledRejection(event); + return; + } + if (isInjectedScriptConnectionError(event?.reason || event)) { + suppressUnhandledRejection(event); + } + }, true); +}; + +const serializeConsoleArgs = (args = []) => { + return args + .map((arg) => { + if (typeof arg === "string") { + return arg; + } + if (arg instanceof Error) { + return arg.stack || arg.message; + } + try { + return JSON.stringify(arg); + } catch (e) { + return String(arg); + } + }) + .join(" "); +}; + +const isUmiDllWarning = (args = []) => { + const message = serializeConsoleArgs(args); + if (!/warning:/i.test(message)) { + return false; + } + const stack = new Error().stack || ""; + return stack.includes("/umi.dll.js"); +}; + +const installConsoleWarningFilter = () => { + if ( + typeof window === "undefined" || + process.env.NODE_ENV === "development" || + window.__consoleWarningFilterInstalled + ) { + return; + } + + window.__consoleWarningFilterInstalled = true; + + ["warn", "error"].forEach((method) => { + const original = console[method]; + if (typeof original !== "function") { + return; + } + console[method] = function(...args) { + if (isUmiDllWarning(args)) { + return; + } + return original.apply(this, args); + }; + }); +}; + +installChunkReloadRecovery(); +installConsoleWarningFilter(); + export async function patchRoutes(routes) { + await refreshApplicationSettings(); const healthRes = await getHealth(); setSetupRequired(`${healthRes?.setup_required}`); + if (getEnterpriseTaskManagerEnabled() !== "true") { + routes = hideRoutesInMenu(routes, ["data_tools"], ""); + } if (getAuthEnabled() === "false") { routes = filterRoutes(routes, ["system.security"], ""); // routes = disableAuth(routes); } + return routes; } export function render(oldRoutes) { + startActivityAwareTokenRefresh(); oldRoutes(); } @@ -64,7 +241,7 @@ export function render(oldRoutes) { function filterRoutes(routes, names, prefix) { return routes.filter((route) => { if (route.name) { - const pn = `${prefix}.${route.name}`; + const pn = prefix ? `${prefix}.${route.name}` : route.name; if (names.includes(pn)) { return false; } @@ -80,7 +257,27 @@ function filterRoutes(routes, names, prefix) { }); } +function hideRoutesInMenu(routes, names, prefix) { + return (routes || []).map((route) => { + const nextRoute = { ...route }; + if (nextRoute.name) { + const pn = prefix ? `${prefix}.${nextRoute.name}` : nextRoute.name; + if (names.includes(pn)) { + nextRoute.hideInMenu = true; + } + } + if (nextRoute.routes) { + let pn = ""; + if (nextRoute.name) { + pn = prefix ? `${prefix}.${nextRoute.name}` : nextRoute.name; + } + nextRoute.routes = hideRoutesInMenu(nextRoute.routes, names, pn); + } + return nextRoute; + }); +} + export function rootContainer(container) { return React.createElement(PlatformContainer, null, container); -} \ No newline at end of file +} diff --git a/web/src/components/GlobalHeader/RightContent.js b/web/src/components/GlobalHeader/RightContent.js index 61a8171cc..663b5a2c8 100644 --- a/web/src/components/GlobalHeader/RightContent.js +++ b/web/src/components/GlobalHeader/RightContent.js @@ -20,8 +20,18 @@ import EmptyNoticeSvg from "@/assets/emptyNotice.svg"; import EmptyTodoSvg from "@/assets/emptyTodo.svg"; import router from "umi/router"; +const isMacPlatform = () => + typeof window !== "undefined" && + /(Mac|iPhone|iPad|iPod)/i.test(window.navigator.platform); + export default class GlobalHeaderRight extends PureComponent { state = { consoleVisible: false, notificationPopupVisible: false }; + + isConsoleToggleShortcut = (event) => { + const key = (event.key || "").toLowerCase(); + return (event.ctrlKey || event.metaKey) && event.shiftKey && (key === "o" || event.keyCode === 79); + }; + getNoticeData() { const { notices = [] } = this.props; if (notices.length === 0) { @@ -43,38 +53,33 @@ export default class GlobalHeaderRight extends PureComponent { this.setState({ consoleVisible: visible, }); + document.body.style.overflow = visible ? "hidden" : ""; var sl = document.querySelector("#root>div"); if (sl) { sl.style.paddingBottom = "0px"; } }; onKeyDown = (e) => { - const { keyCode } = e; - if (this.keysPressed["17"] && this.keysPressed["16"] && keyCode == 79) { + const hasDevtoolPrivilege = + hasAuthority("devtool.console:all") || + hasAuthority("devtool.console:read"); + if (hasDevtoolPrivilege && this.isConsoleToggleShortcut(e)) { + e.preventDefault(); if (this.state.consoleVisible) document.body.style.overflow = ""; this.setConsoleVisible(!this.state.consoleVisible); return true; } - this.keysPressed[keyCode] = e.type == "keydown"; return false; }; - onKeyUp = (e) => { - const { keyCode } = e; - delete this.keysPressed[keyCode]; - }; constructor(props) { super(props); this.onKeyDown = this.onKeyDown.bind(this); - this.onKeyUp = this.onKeyUp.bind(this); } componentDidMount() { - this.keysPressed = {}; document.addEventListener("keydown", this.onKeyDown, false); - document.addEventListener("keyup", this.onKeyUp, false); } componentWillUnmount() { document.removeEventListener("keydown", this.onKeyDown); - document.removeEventListener("keyup", this.onKeyUp); } render() { @@ -132,6 +137,9 @@ export default class GlobalHeaderRight extends PureComponent { const hasDevtoolPrivilege = hasAuthority("devtool.console:all") || hasAuthority("devtool.console:read"); + const consoleShortcutLabel = isMacPlatform() + ? "Cmd+Shift+O" + : "Ctrl+Shift+O"; return (
@@ -167,16 +175,17 @@ export default class GlobalHeaderRight extends PureComponent { {hasDevtoolPrivilege ? ( - { - const { history, selectedCluster } = this.props; - this.setConsoleVisible(!this.state.consoleVisible); - }} - > - {" "} - - + + { + this.setConsoleVisible(!this.state.consoleVisible); + }} + > + + + ) : null} {APP_OFFICIAL_WEBSITE ? ( )}
diff --git a/web/src/components/Licence/index.js b/web/src/components/Licence/index.js index a2f9066d7..f11371dd3 100644 --- a/web/src/components/Licence/index.js +++ b/web/src/components/Licence/index.js @@ -51,7 +51,7 @@ export default forwardRef((props, ref) => { return ( , - placeholder: 'mobile number', + placeholder: formatMessage({ id: 'app.login.mobile.placeholder' }), }, rules: [ { required: true, - message: 'Please enter mobile number!', + message: formatMessage({ id: 'app.login.mobile.required' }), }, { pattern: /^1\d{10}$/, - message: 'Wrong mobile number format!', + message: formatMessage({ id: 'app.login.mobile.invalid' }), }, ], }, @@ -51,12 +52,12 @@ export default { props: { size: 'large', prefix: , - placeholder: 'captcha', + placeholder: formatMessage({ id: 'app.login.captcha.placeholder' }), }, rules: [ { required: true, - message: 'Please enter Captcha!', + message: formatMessage({ id: 'app.login.captcha.required' }), }, ], }, diff --git a/web/src/layouts/BasicLayout.js b/web/src/layouts/BasicLayout.js index 30348ebe4..83b8a1940 100644 --- a/web/src/layouts/BasicLayout.js +++ b/web/src/layouts/BasicLayout.js @@ -19,7 +19,14 @@ import Header from "./Header"; import Context from "./MenuContext"; import Exception403 from "../pages/Exception/403"; import { GlobalContext } from "./GlobalContext"; -import { getAuthEnabled, getAuthority, isLogin } from "@/utils/authority"; +import { + APPLICATION_SETTINGS_UPDATED_EVENT, + getAuthEnabled, + getAuthority, + getEnterpriseTaskManagerEnabled, + isLogin, + refreshApplicationSettings, +} from "@/utils/authority"; import { router, history } from "umi"; import request from "@/utils/request"; import HealthProvider from "@/components/HealthProvider"; @@ -69,7 +76,7 @@ const { Content } = Layout; function formatter(data, parentAuthority, parentName) { return data .map((item) => { - let locale = "menu"; + let locale; if (parentName && item.name) { locale = `${parentName}.${item.name}`; } else if (item.name) { @@ -97,6 +104,19 @@ function formatter(data, parentAuthority, parentName) { .filter((item) => item); } +function filterMenuDataByName(menuData, names = []) { + return (menuData || []).map((item) => { + const nextItem = { ...item }; + if (names.includes(nextItem.name)) { + nextItem.hideInMenu = true; + } + if (nextItem.children) { + nextItem.children = filterMenuDataByName(nextItem.children, names); + } + return nextItem; + }); +} + const memoizeOneFormatter = memoizeOne(formatter, isEqual); const query = { @@ -179,6 +199,29 @@ class BasicLayout extends React.PureComponent { // }); } }); + this.handleDataToolsLicenseRequired = () => { + this.licenceRef?.openToTab?.("license"); + }; + window.addEventListener( + "console:datatools-license-required", + this.handleDataToolsLicenseRequired + ); + this.handleApplicationSettingsUpdated = async () => { + const menuData = this.getMenuData(); + this.setState({ menuData }); + await dispatch({ + type: "global/saveData", + payload: { + menuData, + }, + }); + }; + window.addEventListener( + APPLICATION_SETTINGS_UPDATED_EVENT, + this.handleApplicationSettingsUpdated + ); + await refreshApplicationSettings(true); + await this.handleApplicationSettingsUpdated(); let firstLogin = localStorage.getItem("first-login"); if (firstLogin === "true" && isLogin()) { localStorage.setItem("first-login", false); @@ -240,6 +283,14 @@ class BasicLayout extends React.PureComponent { componentWillUnmount() { cancelAnimationFrame(this.renderRef); unenquireScreen(this.enquireHandler); + window.removeEventListener( + "console:datatools-license-required", + this.handleDataToolsLicenseRequired + ); + window.removeEventListener( + APPLICATION_SETTINGS_UPDATED_EVENT, + this.handleApplicationSettingsUpdated + ); } getContext() { @@ -254,7 +305,11 @@ class BasicLayout extends React.PureComponent { const { route: { routes }, } = this.props; - return memoizeOneFormatter(routes); + let menuData = memoizeOneFormatter(routes); + if (getEnterpriseTaskManagerEnabled() !== "true") { + menuData = filterMenuDataByName(menuData, ["data_tools"]); + } + return menuData; // } @@ -290,8 +345,12 @@ class BasicLayout extends React.PureComponent { if (!currRouterData) { return APP_TITLE; } + const messageId = currRouterData.locale || currRouterData.name; + if (!messageId) { + return APP_TITLE; + } const message = formatMessage({ - id: currRouterData.locale || currRouterData.name, + id: messageId, defaultMessage: currRouterData.name, }); return `${message} - ${APP_TITLE}`; @@ -348,7 +407,16 @@ class BasicLayout extends React.PureComponent { } = this.props; const { isMobile, menuData } = this.state; const isTop = PropsLayout === "topmenu"; + const isDevtoolConsolePage = pathname.startsWith("/devtool/console"); + const hideFooter = isDevtoolConsolePage; const routerConfig = this.matchParamsPath(pathname); + const contentStyle = isDevtoolConsolePage + ? { + ...this.getContentStyle(), + margin: 0, + overflow: "hidden", + } + : this.getContentStyle(); const renderInvalidSecretNotification = () => { const secretMismatch = localStorage.getItem("secret_mismatch"); @@ -389,7 +457,7 @@ class BasicLayout extends React.PureComponent { {...this.props} /> - + } @@ -405,7 +473,7 @@ class BasicLayout extends React.PureComponent { -