1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-08-06 15:43:38 +03:00
parent e5138e7c5d
commit d04c8d3045
11 changed files with 121 additions and 73 deletions

View File

@ -156,11 +156,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
this.activeClusterId = newClusters.has(activeCluster) ? activeCluster : null; this.activeClusterId = newClusters.has(activeCluster) ? activeCluster : null;
this.clusters.replace(newClusters); this.clusters.replace(newClusters);
this.removedClusters.replace(removedClusters); this.removedClusters.replace(removedClusters);
// "auto-select" first cluster if available
if (!this.activeClusterId && newClusters.size) {
this.activeClusterId = Array.from(newClusters.values())[0].id;
}
} }
toJSON(): ClusterStoreModel { toJSON(): ClusterStoreModel {

View File

@ -1,4 +1,4 @@
import { action, computed, observable, reaction, toJS } from "mobx"; import { action, computed, observable, toJS } from "mobx";
import { BaseStore } from "./base-store"; import { BaseStore } from "./base-store";
import { clusterStore } from "./cluster-store" import { clusterStore } from "./cluster-store"
@ -22,15 +22,6 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
super({ super({
configName: "lens-workspace-store", configName: "lens-workspace-store",
}); });
// switch to first available cluster in current workspace
reaction(() => this.currentWorkspaceId, workspaceId => {
const clusters = clusterStore.getByWorkspaceId(workspaceId);
const activeClusterInWorkspace = clusters.some(cluster => cluster.id === clusterStore.activeClusterId);
if (!activeClusterInWorkspace) {
clusterStore.activeClusterId = clusters.length ? clusters[0].id : null;
}
})
} }
@observable currentWorkspaceId = WorkspaceStore.defaultId; @observable currentWorkspaceId = WorkspaceStore.defaultId;

View File

@ -67,11 +67,6 @@ export class Cluster implements ClusterModel {
@observable allowedNamespaces: string[] = []; @observable allowedNamespaces: string[] = [];
@observable allowedResources: string[] = []; @observable allowedResources: string[] = [];
@computed get host() {
const proxyHost = new URL(this.kubeProxyUrl).host;
return `${this.id}.${proxyHost}`
}
constructor(model: ClusterModel) { constructor(model: ClusterModel) {
this.updateModel(model); this.updateModel(model);
} }
@ -223,7 +218,7 @@ export class Cluster implements ClusterModel {
json: true, json: true,
timeout: 5000, timeout: 5000,
headers: { headers: {
Host: this.host, // provide cluster-id for ClusterManager.getClusterForRequest() Host: `${this.id}.${new URL(this.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest()
...(options.headers || {}), ...(options.headers || {}),
}, },
}) })

View File

@ -27,6 +27,7 @@ export class WindowManager {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableRemoteModule: true, enableRemoteModule: true,
webviewTag: true,
}, },
}); });
this.windowState.manage(this.mainView); this.windowState.manage(this.mainView);

View File

@ -1,32 +1,34 @@
import "./cluster-manager.scss" import "./cluster-manager.scss"
import React from "react"; import React from "react";
import { Redirect, Route, Switch } from "react-router";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { ClustersMenu } from "./clusters-menu"; import { ClustersMenu } from "./clusters-menu";
import { BottomBar } from "./bottom-bar"; import { BottomBar } from "./bottom-bar";
import { cssNames, IClassName } from "../../utils"; import { LandingPage, landingRoute, landingURL } from "../+landing-page";
import { ClusterId } from "../../../common/cluster-store";
import { Route, Switch } from "react-router";
import { LandingPage, landingRoute } from "../+landing-page";
import { Preferences, preferencesRoute } from "../+preferences"; import { Preferences, preferencesRoute } from "../+preferences";
import { Workspaces, workspacesRoute } from "../+workspaces"; import { Workspaces, workspacesRoute } from "../+workspaces";
import { AddCluster, addClusterRoute } from "../+add-cluster"; import { AddCluster, addClusterRoute } from "../+add-cluster";
import { ClusterStatus } from "./cluster-status"; import { ClusterView } from "./cluster-view";
import { clusterStatusRoute } from "./cluster-status.route"; import { clusterViewRoute, clusterViewURL } from "./cluster-view.route";
import { clusterStore } from "../../../common/cluster-store";
interface Props {
className?: IClassName;
contentClass?: IClassName;
}
@observer @observer
export class ClusterManager extends React.Component<Props> { export class ClusterManager extends React.Component {
activateView(clusterId: ClusterId) { get startUrl() {
const { activeClusterId } = clusterStore;
if (activeClusterId) {
return clusterViewURL({
params: {
clusterId: activeClusterId
}
})
}
return landingURL()
} }
render() { render() {
const { className } = this.props;
return ( return (
<div className={cssNames("ClusterManager", className)}> <div className="ClusterManager">
<div id="draggable-top"/> <div id="draggable-top"/>
<div id="lens-view"> <div id="lens-view">
<Switch> <Switch>
@ -34,8 +36,8 @@ export class ClusterManager extends React.Component<Props> {
<Route component={Preferences} {...preferencesRoute}/> <Route component={Preferences} {...preferencesRoute}/>
<Route component={Workspaces} {...workspacesRoute}/> <Route component={Workspaces} {...workspacesRoute}/>
<Route component={AddCluster} {...addClusterRoute}/> <Route component={AddCluster} {...addClusterRoute}/>
<Route component={ClusterStatus} {...clusterStatusRoute}/> <Route component={ClusterView} {...clusterViewRoute}/>
<Route render={() => <p>Lens</p>}/> <Redirect exact from="/" to={this.startUrl}/>
</Switch> </Switch>
</div> </div>
<ClustersMenu/> <ClustersMenu/>

View File

@ -1,8 +0,0 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
export const clusterStatusRoute: RouteProps = {
path: "/cluster-status"
}
export const clusterStatusURL = buildURL(clusterStatusRoute.path)

View File

@ -2,37 +2,37 @@ import type { KubeAuthProxyLog } from "../../../main/kube-auth-proxy";
import "./cluster-status.scss" import "./cluster-status.scss"
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { observer } from "mobx-react";
import { ipcRenderer } from "electron"; import { ipcRenderer } from "electron";
import { autorun, computed, observable } from "mobx"; import { computed, observable } from "mobx";
import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterIpc } from "../../../common/cluster-ipc";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Button } from "../button"; import { Button } from "../button";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { navigate } from "../../navigation";
import { Cluster } from "../../../main/cluster"; import { Cluster } from "../../../main/cluster";
import { ClusterId, clusterStore } from "../../../common/cluster-store";
interface Props {
clusterId: ClusterId;
}
@observer @observer
export class ClusterStatus extends React.Component { export class ClusterStatus extends React.Component<Props> {
@observable authOutput: KubeAuthProxyLog[] = []; @observable authOutput: KubeAuthProxyLog[] = [];
@observable isReconnecting = false; @observable isReconnecting = false;
// fixme @computed get clusterId() {
return this.props.clusterId;
}
@computed get cluster(): Cluster { @computed get cluster(): Cluster {
return null; return clusterStore.getById(this.clusterId);
} }
@computed get hasErrors(): boolean { @computed get hasErrors(): boolean {
return this.authOutput.some(({ error }) => error) || !!this.cluster.failureReason; return this.authOutput.some(({ error }) => error) || !!this.cluster.failureReason;
} }
@disposeOnUnmount
autoRedirectToMain = autorun(() => {
if (this.cluster.accessible && !this.hasErrors) {
navigate("/");
}
})
async componentDidMount() { async componentDidMount() {
if (this.cluster.disconnected) { if (this.cluster.disconnected) {
return; return;
@ -48,11 +48,11 @@ export class ClusterStatus extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
ipcRenderer.removeAllListeners(`kube-auth:${this.cluster.id}`); ipcRenderer.removeAllListeners(`kube-auth:${this.clusterId}`);
} }
async refreshClusterState() { async refreshClusterState() {
return clusterIpc.activate.invokeFromRenderer(); return clusterIpc.activate.invokeFromRenderer(this.clusterId);
} }
reconnect = async () => { reconnect = async () => {

View File

@ -0,0 +1,22 @@
import { matchPath, RouteProps } from "react-router";
import { buildURL, navigation } from "../../navigation";
export interface IClusterViewRouteParams {
clusterId: string;
}
export const clusterViewRoute: RouteProps = {
path: "/cluster/:clusterId"
}
export const clusterViewURL = buildURL<IClusterViewRouteParams>(clusterViewRoute.path)
export function getMatchedClusterId(): string {
const matched = matchPath<IClusterViewRouteParams>(navigation.location.pathname, {
...clusterViewRoute,
exact: true,
})
if (matched) {
return matched.params.clusterId;
}
}

View File

@ -0,0 +1,10 @@
.ClusterView {
width: 100%;
height: 100%;
border: 0;
//display: none;
&.loaded {
display: block;
}
}

View File

@ -0,0 +1,46 @@
import React from "react";
import { autorun, computed, observable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { ClusterId, clusterStore } from "../../../common/cluster-store";
import { getMatchedClusterId } from "./cluster-view.route";
import { Cluster } from "../../../main/cluster";
@observer
export class ClusterView extends React.Component {
static views = observable.map<ClusterId, /*HTMLIFrameElement*/ any>()
static isLoaded = observable.map<ClusterId, boolean>()
@computed get cluster() {
return clusterStore.getById(getMatchedClusterId())
}
@computed get clusterView() {
return ClusterView.views.get(this.cluster?.id)
}
componentDidMount() {
disposeOnUnmount(this, [
autorun(() => this.activateView(this.cluster))
])
}
activateView = (cluster: Cluster) => {
if (!cluster || ClusterView.views.has(cluster.id)) {
return;
}
const view = document.createElement("webview");
view.className = "ClusterView"
view.src = `${location.protocol}://${cluster.id}.${location.host}`
view.onload = () => console.log('CLUSTER VIEW READY!', cluster);
document.body.appendChild(view);
ClusterView.views.set(cluster.id, view);
}
render() {
const { cluster } = this;
if (cluster && cluster.accessible) {
}
return "";
}
}

View File

@ -20,7 +20,7 @@ import { landingURL } from "../+landing-page";
import { Tooltip } from "../tooltip"; import { Tooltip } from "../tooltip";
import { ConfirmDialog } from "../confirm-dialog"; import { ConfirmDialog } from "../confirm-dialog";
import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterIpc } from "../../../common/cluster-ipc";
import { clusterStatusURL } from "./cluster-status.route"; import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
// fixme: allow to rearrange clusters with drag&drop // fixme: allow to rearrange clusters with drag&drop
@ -32,12 +32,9 @@ interface Props {
export class ClustersMenu extends React.Component<Props> { export class ClustersMenu extends React.Component<Props> {
@observable showHint = true; @observable showHint = true;
showCluster = (clusterId: ClusterId) => { activateCluster = (clusterId: ClusterId) => {
if (clusterStore.activeClusterId === clusterId) { clusterStore.activeClusterId = clusterId;
navigate("/"); // redirect to index navigate(clusterViewURL({ params: { clusterId } }))
} else {
clusterStore.activeClusterId = clusterId;
}
} }
addCluster = () => { addCluster = () => {
@ -57,9 +54,6 @@ export class ClustersMenu extends React.Component<Props> {
label: _i18n._(t`Disconnect`), label: _i18n._(t`Disconnect`),
click: async () => { click: async () => {
await clusterIpc.disconnect.invokeFromRenderer(cluster.id); await clusterIpc.disconnect.invokeFromRenderer(cluster.id);
if (cluster.id === clusterStore.activeClusterId) {
navigate(clusterStatusURL());
}
} }
})) }))
} }
@ -110,8 +104,8 @@ export class ClustersMenu extends React.Component<Props> {
key={cluster.id} key={cluster.id}
showErrors={true} showErrors={true}
cluster={cluster} cluster={cluster}
isActive={cluster.id === clusterStore.activeClusterId} isActive={cluster.id === getMatchedClusterId()}
onClick={() => this.showCluster(cluster.id)} onClick={() => this.activateCluster(cluster.id)}
onContextMenu={() => this.showContextMenu(cluster)} onContextMenu={() => this.showContextMenu(cluster)}
/> />
) )