mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into fix/isolate-react-select-emotion-styles
This commit is contained in:
commit
b989cc41c8
9
SECURITY.md
Normal file
9
SECURITY.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Team Lens encourages users who become aware of a security vulnerability in Lens to contact Team Lens with details of the vulnerability. Team Lens has established an email address that should be used for reporting a vulnerability. Please send descriptions of any vulnerabilities found to security@k8slens.dev. Please include details on the software and hardware configuration of your system so that we can duplicate the issue being reported.
|
||||
|
||||
Team Lens hopes that users encountering a new vulnerability will contact us privately as it is in the best interests of our users that Team Lens has an opportunity to investigate and confirm a suspected vulnerability before it becomes public knowledge.
|
||||
|
||||
In the case of vulnerabilities found in third-party software components used in Lens, please also notify Team Lens as described above.
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "kontena-lens",
|
||||
"productName": "Lens",
|
||||
"description": "Lens - The Kubernetes IDE",
|
||||
"version": "4.2.0-rc.1",
|
||||
"version": "4.2.0-rc.2",
|
||||
"main": "static/build/main.js",
|
||||
"copyright": "© 2021, Mirantis, Inc.",
|
||||
"license": "MIT",
|
||||
|
||||
@ -28,7 +28,7 @@ export class LensProxy {
|
||||
}
|
||||
|
||||
listen(port = this.port): this {
|
||||
this.proxyServer = this.buildCustomProxy().listen(port);
|
||||
this.proxyServer = this.buildCustomProxy().listen(port, "127.0.0.1");
|
||||
logger.info(`[LENS-PROXY]: Proxy server has started at ${this.origin}`);
|
||||
|
||||
return this;
|
||||
|
||||
@ -2,7 +2,7 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Redirect, Route, Switch } from "react-router";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { crdResourcesRoute, crdRoute, crdURL, crdDefinitionsRoute } from "./crd.route";
|
||||
import { crdDefinitionsRoute, crdResourcesRoute, crdURL } from "./crd.route";
|
||||
import { CrdList } from "./crd-list";
|
||||
import { CrdResources } from "./crd-resources";
|
||||
|
||||
@ -14,7 +14,7 @@ export class CustomResources extends React.Component {
|
||||
title: "Definitions",
|
||||
component: CustomResources,
|
||||
url: crdURL(),
|
||||
routePath: crdRoute.path.toString(),
|
||||
routePath: String(crdDefinitionsRoute.path),
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ const defaultProps: Partial<Props> = {
|
||||
useWorker: false,
|
||||
onBlur: noop,
|
||||
onFocus: noop,
|
||||
cursorPos: { row: 0, column: 0 },
|
||||
};
|
||||
|
||||
@observer
|
||||
|
||||
@ -6,19 +6,26 @@ import { cssNames, prevDefault } from "../../utils";
|
||||
import { observer } from "mobx-react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { Icon } from "../icon";
|
||||
import { TabLayoutRoute } from "./tab-layout";
|
||||
import { sidebarStorage } from "./sidebar-storage";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
|
||||
interface SidebarItemProps {
|
||||
id: string; // Used to save nav item collapse/expand state in local storage
|
||||
/**
|
||||
* Unique id, used in storage and integration tests
|
||||
*/
|
||||
id: string;
|
||||
url: string;
|
||||
text: React.ReactNode | string;
|
||||
className?: string;
|
||||
text: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
isHidden?: boolean;
|
||||
/**
|
||||
* Forces this item to be also show as active or not.
|
||||
*
|
||||
* Default: dynamically checks the location against the `url` props to determine if
|
||||
* this item should be shown as active
|
||||
*/
|
||||
isActive?: boolean;
|
||||
subMenus?: TabLayoutRoute[];
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -26,22 +33,28 @@ export class SidebarItem extends React.Component<SidebarItemProps> {
|
||||
static displayName = "SidebarItem";
|
||||
|
||||
get id(): string {
|
||||
return this.props.id; // unique id, used in storage and integration tests
|
||||
return this.props.id;
|
||||
}
|
||||
|
||||
get compact(): boolean {
|
||||
@computed get compact(): boolean {
|
||||
return Boolean(sidebarStorage.get().compact);
|
||||
}
|
||||
|
||||
get expanded(): boolean {
|
||||
@computed get expanded(): boolean {
|
||||
return Boolean(sidebarStorage.get().expanded[this.id]);
|
||||
}
|
||||
|
||||
@computed get isExpandable(): boolean {
|
||||
const { subMenus, children } = this.props;
|
||||
const hasContent = subMenus?.length > 0 || children;
|
||||
@computed get isActive(): boolean {
|
||||
return this.props.isActive ?? isActiveRoute({
|
||||
path: this.props.url,
|
||||
exact: true,
|
||||
});
|
||||
}
|
||||
|
||||
return Boolean(hasContent && !this.compact) /*not available in compact-mode*/;
|
||||
@computed get isExpandable(): boolean {
|
||||
if (this.compact) return false; // not available in compact-mode currently
|
||||
|
||||
return Boolean(this.props.children);
|
||||
}
|
||||
|
||||
toggleExpand = () => {
|
||||
@ -50,12 +63,26 @@ export class SidebarItem extends React.Component<SidebarItemProps> {
|
||||
});
|
||||
};
|
||||
|
||||
renderSubMenu() {
|
||||
const { isExpandable, expanded, isActive } = this;
|
||||
|
||||
if (!isExpandable || !expanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className={cssNames("sub-menu", { active: isActive })}>
|
||||
{this.props.children}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isHidden, isActive, subMenus = [], icon, text, children, url, className } = this.props;
|
||||
const { isHidden, icon, text, url, className } = this.props;
|
||||
|
||||
if (isHidden) return null;
|
||||
|
||||
const { id, compact, expanded, isExpandable, toggleExpand } = this;
|
||||
const { isActive, id, compact, expanded, isExpandable, toggleExpand } = this;
|
||||
const classNames = cssNames(SidebarItem.displayName, className, {
|
||||
compact,
|
||||
});
|
||||
@ -74,24 +101,7 @@ export class SidebarItem extends React.Component<SidebarItemProps> {
|
||||
material={expanded ? "keyboard_arrow_up" : "keyboard_arrow_down"}
|
||||
/>}
|
||||
</NavLink>
|
||||
{isExpandable && expanded && (
|
||||
<ul className={cssNames("sub-menu", { active: isActive })}>
|
||||
{subMenus.map(({ title, routePath, url = routePath }) => {
|
||||
const subItemId = `${id}${routePath}`;
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
key={subItemId}
|
||||
id={subItemId}
|
||||
url={url}
|
||||
text={title}
|
||||
isActive={isActiveRoute({ path: url, exact: true })}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{children}
|
||||
</ul>
|
||||
)}
|
||||
{this.renderSubMenu()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import "./sidebar.scss";
|
||||
import type { TabLayoutRoute } from "./tab-layout";
|
||||
|
||||
import React from "react";
|
||||
import type { TabLayoutRoute } from "./tab-layout";
|
||||
import { computed } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { cssNames } from "../../utils";
|
||||
@ -22,7 +23,7 @@ import { UserManagement } from "../+user-management";
|
||||
import { Storage } from "../+storage";
|
||||
import { Network } from "../+network";
|
||||
import { crdStore } from "../+custom-resources/crd.store";
|
||||
import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources";
|
||||
import { crdRoute, crdURL } from "../+custom-resources";
|
||||
import { CustomResources } from "../+custom-resources/custom-resources";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
@ -44,29 +45,45 @@ export class Sidebar extends React.Component<Props> {
|
||||
crdStore.reloadAll();
|
||||
}
|
||||
|
||||
renderCustomResources() {
|
||||
if (crdStore.isLoading) {
|
||||
@computed get crdSubMenus(): React.ReactNode {
|
||||
if (!crdStore.isLoaded && crdStore.isLoading) {
|
||||
return <Spinner centerHorizontal/>;
|
||||
}
|
||||
|
||||
return Object.entries(crdStore.groups).map(([group, crds]) => {
|
||||
const submenus: TabLayoutRoute[] = crds.map((crd) => {
|
||||
return {
|
||||
title: crd.getResourceKind(),
|
||||
component: CrdList,
|
||||
url: crd.getResourceUrl(),
|
||||
routePath: String(crdResourcesRoute.path),
|
||||
};
|
||||
});
|
||||
const id = `crd-group:${group}`;
|
||||
const crdGroupsPageUrl = crdURL({ query: { groups: group } });
|
||||
|
||||
return (
|
||||
<SidebarItem key={id} id={id} text={group} url={crdGroupsPageUrl}>
|
||||
{crds.map((crd) => (
|
||||
<SidebarItem
|
||||
key={crd.getResourceApiBase()}
|
||||
id={`crd-resource:${crd.getResourceApiBase()}`}
|
||||
url={crd.getResourceUrl()}
|
||||
text={crd.getResourceTitle()}
|
||||
/>
|
||||
))}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderTreeFromTabRoutes(tabRoutes: TabLayoutRoute[] = []): React.ReactNode {
|
||||
if (!tabRoutes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tabRoutes.map(({ title, routePath, url = routePath, exact = true }) => {
|
||||
const subMenuItemId = `tab-route-item-${url}`;
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
key={group}
|
||||
id={`crd-group:${group}`}
|
||||
url={crdURL({ query: { groups: group } })}
|
||||
subMenus={submenus}
|
||||
text={group}
|
||||
isActive={false}
|
||||
key={subMenuItemId}
|
||||
id={subMenuItemId}
|
||||
url={url}
|
||||
text={title}
|
||||
isActive={isActiveRoute({ path: routePath, exact })}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -122,11 +139,12 @@ export class Sidebar extends React.Component<Props> {
|
||||
key={id}
|
||||
id={id}
|
||||
url={pageUrl}
|
||||
isActive={isActive}
|
||||
text={menuItem.title}
|
||||
icon={<menuItem.components.Icon/>}
|
||||
isActive={isActive}
|
||||
subMenus={tabRoutes}
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(tabRoutes)}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -153,98 +171,104 @@ export class Sidebar extends React.Component<Props> {
|
||||
<div className={cssNames("sidebar-nav flex column box grow-fixed", { compact })}>
|
||||
<SidebarItem
|
||||
id="cluster"
|
||||
text="Cluster"
|
||||
isActive={isActiveRoute(clusterRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={clusterURL()}
|
||||
text="Cluster"
|
||||
icon={<Icon svg="kube"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="nodes"
|
||||
text="Nodes"
|
||||
isActive={isActiveRoute(nodesRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={nodesURL()}
|
||||
text="Nodes"
|
||||
icon={<Icon svg="nodes"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="workloads"
|
||||
text="Workloads"
|
||||
isActive={isActiveRoute(workloadsRoute)}
|
||||
isHidden={Workloads.tabRoutes.length == 0}
|
||||
url={workloadsURL({ query })}
|
||||
subMenus={Workloads.tabRoutes}
|
||||
text="Workloads"
|
||||
icon={<Icon svg="workloads"/>}
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Workloads.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="config"
|
||||
text="Configuration"
|
||||
isActive={isActiveRoute(configRoute)}
|
||||
isHidden={Config.tabRoutes.length == 0}
|
||||
url={configURL({ query })}
|
||||
subMenus={Config.tabRoutes}
|
||||
text="Configuration"
|
||||
icon={<Icon material="list"/>}
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Config.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="networks"
|
||||
text="Network"
|
||||
isActive={isActiveRoute(networkRoute)}
|
||||
isHidden={Network.tabRoutes.length == 0}
|
||||
url={networkURL({ query })}
|
||||
subMenus={Network.tabRoutes}
|
||||
text="Network"
|
||||
icon={<Icon material="device_hub"/>}
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Network.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="storage"
|
||||
text="Storage"
|
||||
isActive={isActiveRoute(storageRoute)}
|
||||
isHidden={Storage.tabRoutes.length == 0}
|
||||
url={storageURL({ query })}
|
||||
subMenus={Storage.tabRoutes}
|
||||
icon={<Icon svg="storage"/>}
|
||||
text="Storage"
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Storage.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="namespaces"
|
||||
text="Namespaces"
|
||||
isActive={isActiveRoute(namespacesRoute)}
|
||||
isHidden={!isAllowedResource("namespaces")}
|
||||
url={namespacesURL()}
|
||||
icon={<Icon material="layers"/>}
|
||||
text="Namespaces"
|
||||
/>
|
||||
<SidebarItem
|
||||
id="events"
|
||||
text="Events"
|
||||
isActive={isActiveRoute(eventRoute)}
|
||||
isHidden={!isAllowedResource("events")}
|
||||
url={eventsURL({ query })}
|
||||
icon={<Icon material="access_time"/>}
|
||||
text="Events"
|
||||
/>
|
||||
<SidebarItem
|
||||
id="apps"
|
||||
text="Apps" // helm charts
|
||||
isActive={isActiveRoute(appsRoute)}
|
||||
url={appsURL({ query })}
|
||||
subMenus={Apps.tabRoutes}
|
||||
icon={<Icon material="apps"/>}
|
||||
text="Apps"
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Apps.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="users"
|
||||
text="Access Control"
|
||||
isActive={isActiveRoute(usersManagementRoute)}
|
||||
url={usersManagementURL({ query })}
|
||||
subMenus={UserManagement.tabRoutes}
|
||||
icon={<Icon material="security"/>}
|
||||
text="Access Control"
|
||||
/>
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(UserManagement.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="custom-resources"
|
||||
text="Custom Resources"
|
||||
url={crdURL()}
|
||||
isActive={isActiveRoute(crdRoute)}
|
||||
isHidden={!isAllowedResource("customresourcedefinitions")}
|
||||
url={crdURL()}
|
||||
subMenus={CustomResources.tabRoutes}
|
||||
icon={<Icon material="extension"/>}
|
||||
text="Custom Resources"
|
||||
>
|
||||
{this.renderCustomResources()}
|
||||
{this.renderTreeFromTabRoutes(CustomResources.tabRoutes)}
|
||||
{this.crdSubMenus}
|
||||
</SidebarItem>
|
||||
{this.renderRegisteredMenus()}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
||||
|
||||
## 4.2.0-rc.1 (current version)
|
||||
## 4.2.0-rc.2 (current version)
|
||||
|
||||
- Add lens:// protocol handling with a routing mechanism
|
||||
- Add common app routes to the protocol renderer router from the documentation
|
||||
@ -18,6 +18,8 @@ Here you can find description of changes we've built into each release. While we
|
||||
- Navigation menu in Preferences
|
||||
- Add terminal clear shortcut for macOS
|
||||
- Add the ability to hide metrics from the UI
|
||||
- Add notification to user to add accessible namespaces when needed
|
||||
- Change Cluster Settings button to be a menu like cluster icon menu
|
||||
- Fix: Proper sorting resources by age column
|
||||
- Fix: Events sorting with compact=true is broken
|
||||
- Fix: Two charts refer to an arbitrary repository
|
||||
@ -30,6 +32,7 @@ Here you can find description of changes we've built into each release. While we
|
||||
- Fix: Extension command palette loading
|
||||
- Fix: Closing workspace menu after clicking on iframe
|
||||
- Fix: extension global pages are never able to be visible
|
||||
- Fix: recreate proxy kubeconfig if it is deleted
|
||||
|
||||
## 4.1.4
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user