mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
375 lines
13 KiB
TypeScript
375 lines
13 KiB
TypeScript
/**
|
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
*/
|
|
|
|
// Main process
|
|
|
|
import { injectSystemCAs } from "../common/system-ca";
|
|
import * as Mobx from "mobx";
|
|
import * as LensExtensionsCommonApi from "../extensions/common-api";
|
|
import * as LensExtensionsMainApi from "../extensions/main-api";
|
|
import { app, autoUpdater, dialog, powerMonitor } from "electron";
|
|
import { appName, isIntegrationTesting, isMac, isWindows, productName, isDevelopment } from "../common/vars";
|
|
import { LensProxy } from "./lens-proxy";
|
|
import { WindowManager } from "./window-manager";
|
|
import { ClusterManager } from "./cluster-manager";
|
|
import { shellSync } from "./shell-sync";
|
|
import { mangleProxyEnv } from "./proxy-env";
|
|
import { registerFileProtocol } from "../common/register-protocol";
|
|
import logger from "./logger";
|
|
import { appEventBus } from "../common/app-event-bus/event-bus";
|
|
import type { InstalledExtension } from "../extensions/extension-discovery/extension-discovery";
|
|
import type { LensExtensionId } from "../extensions/lens-extension";
|
|
import { installDeveloperTools } from "./developer-tools";
|
|
import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
|
import { ipcMainOn } from "../common/ipc";
|
|
import { startUpdateChecking } from "./app-updater";
|
|
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
|
import { startCatalogSyncToRenderer } from "./catalog-pusher";
|
|
import { catalogEntityRegistry } from "./catalog";
|
|
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
|
import { syncGeneralEntities, syncWeblinks } from "./catalog-sources";
|
|
import configurePackages from "../common/configure-packages";
|
|
import { PrometheusProviderRegistry } from "./prometheus";
|
|
import * as initializers from "./initializers";
|
|
import { HotbarStore } from "../common/hotbar-store";
|
|
import { WeblinkStore } from "../common/weblink-store";
|
|
import { SentryInit } from "../common/sentry";
|
|
import { ensureDir } from "fs-extra";
|
|
import { initMenu } from "./menu/menu";
|
|
import { kubeApiRequest } from "./proxy-functions";
|
|
import { initTray } from "./tray/tray";
|
|
import { ShellSession } from "./shell-session/shell-session";
|
|
import { getDi } from "./getDi";
|
|
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
|
|
import lensProtocolRouterMainInjectable from "./protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
|
|
import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable";
|
|
import directoryForExesInjectable from "../common/app-paths/directory-for-exes/directory-for-exes.injectable";
|
|
import initIpcMainHandlersInjectable from "./initializers/init-ipc-main-handlers/init-ipc-main-handlers.injectable";
|
|
import electronMenuItemsInjectable from "./menu/electron-menu-items.injectable";
|
|
import directoryForKubeConfigsInjectable from "../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
|
import kubeconfigSyncManagerInjectable from "./catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable";
|
|
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
|
|
import routerInjectable from "./router/router.injectable";
|
|
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
|
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
|
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
|
|
|
|
const di = getDi();
|
|
|
|
app.setName(appName);
|
|
|
|
di.runSetups().then(() => {
|
|
injectSystemCAs();
|
|
|
|
const onCloseCleanup = disposer();
|
|
const onQuitCleanup = disposer();
|
|
|
|
SentryInit();
|
|
|
|
logger.info(`📟 Setting ${productName} as protocol client for lens://`);
|
|
|
|
if (app.setAsDefaultProtocolClient("lens")) {
|
|
logger.info("📟 Protocol client register succeeded ✅");
|
|
} else {
|
|
logger.info("📟 Protocol client register failed ❗");
|
|
}
|
|
|
|
if (process.env.LENS_DISABLE_GPU) {
|
|
app.disableHardwareAcceleration();
|
|
}
|
|
|
|
logger.debug("[APP-MAIN] configuring packages");
|
|
configurePackages();
|
|
|
|
mangleProxyEnv();
|
|
|
|
const initIpcMainHandlers = di.inject(initIpcMainHandlersInjectable);
|
|
|
|
logger.debug("[APP-MAIN] initializing ipc main handlers");
|
|
initIpcMainHandlers();
|
|
|
|
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
|
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server");
|
|
}
|
|
|
|
logger.debug("[APP-MAIN] Lens protocol routing main");
|
|
|
|
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
|
|
|
|
if (!app.requestSingleInstanceLock()) {
|
|
app.exit();
|
|
} else {
|
|
for (const arg of process.argv) {
|
|
if (arg.toLowerCase().startsWith("lens://")) {
|
|
lensProtocolRouterMain.route(arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
app.on("second-instance", (event, argv) => {
|
|
logger.debug("second-instance message");
|
|
|
|
for (const arg of argv) {
|
|
if (arg.toLowerCase().startsWith("lens://")) {
|
|
lensProtocolRouterMain.route(arg);
|
|
}
|
|
}
|
|
|
|
WindowManager.getInstance(false)?.ensureMainWindow();
|
|
});
|
|
|
|
app.on("ready", async () => {
|
|
const directoryForExes = di.inject(directoryForExesInjectable);
|
|
|
|
logger.info(`🚀 Starting ${productName} from "${directoryForExes}"`);
|
|
logger.info("🐚 Syncing shell environment");
|
|
await shellSync();
|
|
|
|
powerMonitor.on("shutdown", () => app.exit());
|
|
|
|
registerFileProtocol("static", __static);
|
|
|
|
PrometheusProviderRegistry.createInstance();
|
|
initializers.initPrometheusProviderRegistry();
|
|
|
|
/**
|
|
* The following sync MUST be done before HotbarStore creation, because that
|
|
* store has migrations that will remove items that previous migrations add
|
|
* if this is not present
|
|
*/
|
|
syncGeneralEntities();
|
|
|
|
logger.info("💾 Loading stores");
|
|
|
|
const userStore = di.inject(userStoreInjectable);
|
|
|
|
userStore.startMainReactions();
|
|
|
|
// ClusterStore depends on: UserStore
|
|
const clusterStore = di.inject(clusterStoreInjectable);
|
|
|
|
clusterStore.provideInitialFromMain();
|
|
|
|
// HotbarStore depends on: ClusterStore
|
|
HotbarStore.createInstance();
|
|
|
|
WeblinkStore.createInstance();
|
|
|
|
syncWeblinks();
|
|
|
|
HelmRepoManager.createInstance(); // create the instance
|
|
|
|
const router = di.inject(routerInjectable);
|
|
const shellApiRequest = di.inject(shellApiRequestInjectable);
|
|
|
|
const lensProxy = LensProxy.createInstance(router, {
|
|
getClusterForRequest: (req) => ClusterManager.getInstance().getClusterForRequest(req),
|
|
kubeApiRequest,
|
|
shellApiRequest,
|
|
});
|
|
|
|
ClusterManager.createInstance().init();
|
|
|
|
initializers.initClusterMetadataDetectors();
|
|
|
|
try {
|
|
logger.info("🔌 Starting LensProxy");
|
|
await lensProxy.listen(); // lensProxy.port available
|
|
} catch (error) {
|
|
dialog.showErrorBox("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);
|
|
|
|
return app.exit();
|
|
}
|
|
|
|
// test proxy connection
|
|
try {
|
|
logger.info("🔎 Testing LensProxy connection ...");
|
|
const versionFromProxy = await getAppVersionFromProxyServer(lensProxy.port);
|
|
|
|
if (getAppVersion() !== versionFromProxy) {
|
|
logger.error("Proxy server responded with invalid response");
|
|
|
|
return app.exit();
|
|
}
|
|
|
|
logger.info("⚡ LensProxy connection OK");
|
|
} catch (error) {
|
|
logger.error(`🛑 LensProxy: failed connection test: ${error}`);
|
|
|
|
const hostsPath = isWindows
|
|
? "C:\\windows\\system32\\drivers\\etc\\hosts"
|
|
: "/etc/hosts";
|
|
const message = [
|
|
`Failed connection test: ${error}`,
|
|
"Check to make sure that no other versions of Lens are running",
|
|
`Check ${hostsPath} to make sure that it is clean and that the localhost loopback is at the top and set to 127.0.0.1`,
|
|
"If you have HTTP_PROXY or http_proxy set in your environment, make sure that the localhost and the ipv4 loopback address 127.0.0.1 are added to the NO_PROXY environment variable.",
|
|
];
|
|
|
|
dialog.showErrorBox("Lens Proxy Error", message.join("\n\n"));
|
|
|
|
return app.exit();
|
|
}
|
|
|
|
const extensionLoader = di.inject(extensionLoaderInjectable);
|
|
|
|
extensionLoader.init();
|
|
|
|
const extensionDiscovery = di.inject(extensionDiscoveryInjectable);
|
|
|
|
extensionDiscovery.init();
|
|
|
|
// Start the app without showing the main window when auto starting on login
|
|
// (On Windows and Linux, we get a flag. On MacOS, we get special API.)
|
|
const startHidden = process.argv.includes("--hidden") || (isMac && app.getLoginItemSettings().wasOpenedAsHidden);
|
|
|
|
logger.info("🖥️ Starting WindowManager");
|
|
const windowManager = WindowManager.createInstance();
|
|
|
|
// Override main content view url to local webpack-dev-server to support HMR / live-reload
|
|
if (isDevelopment) {
|
|
const { createDevServer } = await import("../../webpack.dev-server");
|
|
const devServer = createDevServer(lensProxy.port);
|
|
|
|
await devServer.start();
|
|
windowManager.mainContentUrl = `http://localhost:${devServer.options.port}`;
|
|
}
|
|
|
|
const menuItems = di.inject(electronMenuItemsInjectable);
|
|
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
|
|
|
onQuitCleanup.push(
|
|
initMenu(windowManager, menuItems),
|
|
initTray(windowManager, trayMenuItems),
|
|
() => ShellSession.cleanup(),
|
|
);
|
|
|
|
installDeveloperTools();
|
|
|
|
if (!startHidden) {
|
|
windowManager.ensureMainWindow();
|
|
}
|
|
|
|
ipcMainOn(IpcRendererNavigationEvents.LOADED, async () => {
|
|
onCloseCleanup.push(startCatalogSyncToRenderer(catalogEntityRegistry));
|
|
|
|
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
|
|
|
await ensureDir(directoryForKubeConfigs);
|
|
|
|
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
|
|
|
|
kubeConfigSyncManager.startSync();
|
|
|
|
startUpdateChecking();
|
|
lensProtocolRouterMain.rendererLoaded = true;
|
|
});
|
|
|
|
logger.info("🧩 Initializing extensions");
|
|
|
|
// call after windowManager to see splash earlier
|
|
try {
|
|
const extensions = await extensionDiscovery.load();
|
|
|
|
// Start watching after bundled extensions are loaded
|
|
extensionDiscovery.watchExtensions();
|
|
|
|
// Subscribe to extensions that are copied or deleted to/from the extensions folder
|
|
extensionDiscovery.events
|
|
.on("add", (extension: InstalledExtension) => {
|
|
extensionLoader.addExtension(extension);
|
|
})
|
|
.on("remove", (lensExtensionId: LensExtensionId) => {
|
|
extensionLoader.removeExtension(lensExtensionId);
|
|
});
|
|
|
|
extensionLoader.initExtensions(extensions);
|
|
} catch (error) {
|
|
dialog.showErrorBox("Lens Error", `Could not load extensions${error?.message ? `: ${error.message}` : ""}`);
|
|
console.error(error);
|
|
console.trace();
|
|
}
|
|
|
|
setTimeout(() => {
|
|
appEventBus.emit({ name: "service", action: "start" });
|
|
}, 1000);
|
|
});
|
|
|
|
app.on("activate", (event, hasVisibleWindows) => {
|
|
logger.info("APP:ACTIVATE", { hasVisibleWindows });
|
|
|
|
if (!hasVisibleWindows) {
|
|
WindowManager.getInstance(false)?.ensureMainWindow(false);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* This variable should is used so that `autoUpdater.installAndQuit()` works
|
|
*/
|
|
let blockQuit = !isIntegrationTesting;
|
|
|
|
autoUpdater.on("before-quit-for-update", () => {
|
|
logger.debug("Unblocking quit for update");
|
|
blockQuit = false;
|
|
});
|
|
|
|
app.on("will-quit", (event) => {
|
|
logger.debug("will-quit message");
|
|
|
|
// This is called when the close button of the main window is clicked
|
|
|
|
|
|
logger.info("APP:QUIT");
|
|
appEventBus.emit({ name: "app", action: "close" });
|
|
ClusterManager.getInstance(false)?.stop(); // close cluster connections
|
|
|
|
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
|
|
|
|
kubeConfigSyncManager.stopSync();
|
|
|
|
onCloseCleanup();
|
|
|
|
// This is set to false here so that LPRM can wait to send future lens://
|
|
// requests until after it loads again
|
|
lensProtocolRouterMain.rendererLoaded = false;
|
|
|
|
if (blockQuit) {
|
|
// Quit app on Cmd+Q (MacOS)
|
|
|
|
event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.)
|
|
|
|
return; // skip exit to make tray work, to quit go to app's global menu or tray's menu
|
|
}
|
|
|
|
lensProtocolRouterMain.cleanup();
|
|
onQuitCleanup();
|
|
});
|
|
|
|
app.on("open-url", (event, rawUrl) => {
|
|
logger.debug("open-url message");
|
|
|
|
// lens:// protocol handler
|
|
event.preventDefault();
|
|
lensProtocolRouterMain.route(rawUrl);
|
|
});
|
|
|
|
logger.debug("[APP-MAIN] waiting for 'ready' and other messages");
|
|
});
|
|
|
|
/**
|
|
* Exports for virtual package "@k8slens/extensions" for main-process.
|
|
* All exporting names available in global runtime scope:
|
|
* e.g. global.Mobx, global.LensExtensions
|
|
*/
|
|
const LensExtensions = {
|
|
Common: LensExtensionsCommonApi,
|
|
Main: LensExtensionsMainApi,
|
|
};
|
|
|
|
export {
|
|
Mobx,
|
|
LensExtensions,
|
|
};
|