mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
more work
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
3cf0538905
commit
170e19e7cd
@ -11,7 +11,7 @@ import { appEventBus } from "./event-bus";
|
|||||||
import { dumpConfigYaml } from "./kube-helpers";
|
import { dumpConfigYaml } from "./kube-helpers";
|
||||||
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||||
import { KubeConfig } from "@kubernetes/client-node";
|
import { KubeConfig } from "@kubernetes/client-node";
|
||||||
import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
|
import { createTypedInvoker, isEmptyArgs, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import move from "array-move";
|
import move from "array-move";
|
||||||
import type { WorkspaceId } from "./workspace-store";
|
import type { WorkspaceId } from "./workspace-store";
|
||||||
@ -90,6 +90,24 @@ export interface ClusterPrometheusPreferences {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ClusterStateSync {
|
||||||
|
id: string;
|
||||||
|
state: ClusterState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClusterStoreStateHandler(): ClusterStateSync[] {
|
||||||
|
return clusterStore.clustersList.map(cluster => ({
|
||||||
|
state: cluster.getState(),
|
||||||
|
id: cluster.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const clusterStoreStateRequest = createTypedInvoker({
|
||||||
|
channel: "cluster:states",
|
||||||
|
handler: ClusterStoreStateHandler,
|
||||||
|
verifier: isEmptyArgs,
|
||||||
|
});
|
||||||
|
|
||||||
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||||
static getCustomKubeConfigPath(clusterId: ClusterId): string {
|
static getCustomKubeConfigPath(clusterId: ClusterId): string {
|
||||||
return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs", clusterId);
|
return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs", clusterId);
|
||||||
@ -108,8 +126,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
||||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
@observable clusters = observable.map<ClusterId, Cluster>();
|
||||||
|
|
||||||
private static stateRequestChannel = "cluster:states";
|
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
super({
|
super({
|
||||||
configName: "lens-cluster-store",
|
configName: "lens-cluster-store",
|
||||||
@ -125,35 +141,13 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
await super.load();
|
await super.load();
|
||||||
type clusterStateSync = {
|
|
||||||
id: string;
|
|
||||||
state: ClusterState;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
logger.info("[CLUSTER-STORE] requesting initial state sync");
|
logger.info("[CLUSTER-STORE] requesting initial state sync");
|
||||||
const clusterStates: clusterStateSync[] = await requestMain(ClusterStore.stateRequestChannel);
|
|
||||||
|
|
||||||
clusterStates.forEach((clusterState) => {
|
for (const { id, state } of await clusterStoreStateRequest.invoke()) {
|
||||||
const cluster = this.getById(clusterState.id);
|
this.getById(id)?.setState(state);
|
||||||
|
|
||||||
if (cluster) {
|
|
||||||
cluster.setState(clusterState.state);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
|
||||||
handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => {
|
|
||||||
const states: clusterStateSync[] = [];
|
|
||||||
|
|
||||||
this.clustersList.forEach((cluster) => {
|
|
||||||
states.push({
|
|
||||||
state: cluster.getState(),
|
|
||||||
id: cluster.id
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return states;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,13 @@ import { ipcMain, ipcRenderer, webContents, remote } from "electron";
|
|||||||
import { toJS } from "mobx";
|
import { toJS } from "mobx";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
||||||
|
import { createTypedInvoker, isEmptyArgs } from "./type-enforced-ipc";
|
||||||
|
|
||||||
const subFramesChannel = "ipc:get-sub-frames";
|
const subFrames = createTypedInvoker({
|
||||||
|
channel: "ipc:get-sub-frames",
|
||||||
|
handler: getSubFrames,
|
||||||
|
verifier: isEmptyArgs,
|
||||||
|
});
|
||||||
|
|
||||||
export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
|
export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||||
ipcMain.handle(channel, listener);
|
ipcMain.handle(channel, listener);
|
||||||
@ -39,11 +44,11 @@ export async function broadcastMessage(channel: string, ...args: any[]) {
|
|||||||
view.send(channel, ...args);
|
view.send(channel, ...args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const subFrames: ClusterFrameInfo[] = ipcRenderer
|
const childFrames: ClusterFrameInfo[] = ipcRenderer
|
||||||
? await requestMain(subFramesChannel)
|
? await subFrames.invoke()
|
||||||
: getSubFrames();
|
: getSubFrames();
|
||||||
|
|
||||||
for (const frameInfo of subFrames) {
|
for (const frameInfo of childFrames) {
|
||||||
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -77,9 +82,3 @@ export function unsubscribeAllFromBroadcast(channel: string) {
|
|||||||
ipcMain.removeAllListeners(channel);
|
ipcMain.removeAllListeners(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindBroadcastHandlers() {
|
|
||||||
handleRequest(subFramesChannel, () => {
|
|
||||||
return getSubFrames();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@ export type ListVerifier<T extends any[]> = (args: unknown[]) => args is T;
|
|||||||
export type Rest<T> = T extends [any, ...infer R] ? R : [];
|
export type Rest<T> = T extends [any, ...infer R] ? R : [];
|
||||||
export type IpcListener<E extends Event, Args extends any[]> = (e: E, ...args: Args) => void;
|
export type IpcListener<E extends Event, Args extends any[]> = (e: E, ...args: Args) => void;
|
||||||
|
|
||||||
|
export function isEmptyArgs(args: unknown[]): args is [] {
|
||||||
|
return args.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener to `source` that waits for the first IPC message with the correct
|
* Adds a listener to `source` that waits for the first IPC message with the correct
|
||||||
* argument data is sent.
|
* argument data is sent.
|
||||||
@ -210,7 +214,7 @@ export function createTypedSender<
|
|||||||
source: ipcMain ?? ipcRenderer,
|
source: ipcMain ?? ipcRenderer,
|
||||||
channel,
|
channel,
|
||||||
listener,
|
listener,
|
||||||
verifier: verifier as any, // safety: this verifier is correct, TS just doesn't equate Rest<..> correctly
|
verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
once(listener) {
|
once(listener) {
|
||||||
@ -218,7 +222,7 @@ export function createTypedSender<
|
|||||||
source: ipcMain ?? ipcRenderer,
|
source: ipcMain ?? ipcRenderer,
|
||||||
channel,
|
channel,
|
||||||
listener,
|
listener,
|
||||||
verifier: verifier as any, // safety: this verifier is correct, TS just doesn't equate Rest<..> correctly
|
verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -107,8 +107,14 @@ export function isNull(val: unknown): val is null {
|
|||||||
* This is useful for when using `hasOptionalProperty` and `hasTypedProperty`
|
* This is useful for when using `hasOptionalProperty` and `hasTypedProperty`
|
||||||
* @param fn A typescript user predicate function to be bound
|
* @param fn A typescript user predicate function to be bound
|
||||||
* @param boundArgs the set of arguments to be passed to `fn` in the new function
|
* @param boundArgs the set of arguments to be passed to `fn` in the new function
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* bindTypeGuard(isTypedArray, isString); // Predicate<string[]>
|
||||||
|
* bindTypeGuard(isRecord, isString, isBoolean); // Predicate<Record<string, boolean>>
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
export function bindTypeGuard<FnArgs extends any[], T>(fn: (arg1: unknown, ...args: FnArgs) => arg1 is T, ...boundArgs: FnArgs): (arg1: unknown) => arg1 is T {
|
export function bindTypeGuard<FnArgs extends any[], T>(fn: (arg1: unknown, ...args: FnArgs) => arg1 is T, ...boundArgs: FnArgs): Predicate<T> {
|
||||||
return (arg1: unknown): arg1 is T => fn(arg1, ...boundArgs);
|
return (arg1: unknown): arg1 is T => fn(arg1, ...boundArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +128,11 @@ type OrReturnPredicateType<T extends Predicate<any>[]> = ReturnPredicateType<Fir
|
|||||||
* Create a new type-guard for the union of the types that each of the
|
* Create a new type-guard for the union of the types that each of the
|
||||||
* predicates are type-guarding for
|
* predicates are type-guarding for
|
||||||
* @param predicates a list of predicates that should be executed in order
|
* @param predicates a list of predicates that should be executed in order
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* createUnionGuard(isString, isBoolean); // Predicate<string | boolean>
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
export function createUnionGuard<Predicates extends Predicate<any>[]>(...predicates: Predicates): Predicate<OrReturnPredicateType<Predicates>> {
|
export function createUnionGuard<Predicates extends Predicate<any>[]>(...predicates: Predicates): Predicate<OrReturnPredicateType<Predicates>> {
|
||||||
return (arg: unknown): arg is OrReturnPredicateType<Predicates> => {
|
return (arg: unknown): arg is OrReturnPredicateType<Predicates> => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { action, computed, observable, toJS, reaction } from "mobx";
|
|||||||
import { BaseStore } from "./base-store";
|
import { BaseStore } from "./base-store";
|
||||||
import { clusterStore } from "./cluster-store";
|
import { clusterStore } from "./cluster-store";
|
||||||
import { appEventBus } from "./event-bus";
|
import { appEventBus } from "./event-bus";
|
||||||
import { broadcastMessage, handleRequest, requestMain } from "../common/ipc";
|
import { broadcastMessage, createTypedInvoker, isEmptyArgs } from "../common/ipc";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import type { ClusterId } from "./cluster-store";
|
import type { ClusterId } from "./cluster-store";
|
||||||
|
|
||||||
@ -141,9 +141,26 @@ export class Workspace implements WorkspaceModel, WorkspaceState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WorkspaceStateSync {
|
||||||
|
id: string;
|
||||||
|
state: WorkspaceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkspaceStoreStateHandler(): WorkspaceStateSync[] {
|
||||||
|
return clusterStore.clustersList.map(cluster => ({
|
||||||
|
state: cluster.getState(),
|
||||||
|
id: cluster.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceStoreStateRequest = createTypedInvoker({
|
||||||
|
channel: "workspace:states",
|
||||||
|
handler: WorkspaceStoreStateHandler,
|
||||||
|
verifier: isEmptyArgs,
|
||||||
|
});
|
||||||
|
|
||||||
export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||||
static readonly defaultId: WorkspaceId = "default";
|
static readonly defaultId: WorkspaceId = "default";
|
||||||
private static stateRequestChannel = "workspace:states";
|
|
||||||
|
|
||||||
@observable currentWorkspaceId = WorkspaceStore.defaultId;
|
@observable currentWorkspaceId = WorkspaceStore.defaultId;
|
||||||
@observable workspaces = observable.map<WorkspaceId, Workspace>();
|
@observable workspaces = observable.map<WorkspaceId, Workspace>();
|
||||||
@ -161,35 +178,13 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
await super.load();
|
await super.load();
|
||||||
type workspaceStateSync = {
|
|
||||||
id: string;
|
|
||||||
state: WorkspaceState;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
logger.info("[WORKSPACE-STORE] requesting initial state sync");
|
logger.info("[WORKSPACE-STORE] requesting initial state sync");
|
||||||
const workspaceStates: workspaceStateSync[] = await requestMain(WorkspaceStore.stateRequestChannel);
|
|
||||||
|
|
||||||
workspaceStates.forEach((workspaceState) => {
|
for (const { id, state } of await workspaceStoreStateRequest.invoke()) {
|
||||||
const workspace = this.getById(workspaceState.id);
|
this.getById(id)?.setState(state);
|
||||||
|
|
||||||
if (workspace) {
|
|
||||||
workspace.setState(workspaceState.state);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
|
||||||
handleRequest(WorkspaceStore.stateRequestChannel, (): workspaceStateSync[] => {
|
|
||||||
const states: workspaceStateSync[] = [];
|
|
||||||
|
|
||||||
this.workspacesList.forEach((workspace) => {
|
|
||||||
states.push({
|
|
||||||
state: workspace.getState(),
|
|
||||||
id: workspace.id
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return states;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import fs from "fs-extra";
|
|||||||
import { observable, reaction, toJS, when } from "mobx";
|
import { observable, reaction, toJS, when } from "mobx";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc";
|
import { createTypedInvoker, createTypedSender, isEmptyArgs } from "../common/ipc";
|
||||||
import { getBundledExtensions } from "../common/utils/app-version";
|
import { getBundledExtensions } from "../common/utils/app-version";
|
||||||
|
import { hasTypedProperty, isBoolean } from "../common/utils/type-narrowing";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { extensionInstaller, PackageJson } from "./extension-installer";
|
import { extensionInstaller, PackageJson } from "./extension-installer";
|
||||||
import { extensionsStore } from "./extensions-store";
|
import { extensionsStore } from "./extensions-store";
|
||||||
@ -31,15 +32,35 @@ const logModule = "[EXTENSION-DISCOVERY]";
|
|||||||
|
|
||||||
export const manifestFilename = "package.json";
|
export const manifestFilename = "package.json";
|
||||||
|
|
||||||
interface ExtensionDiscoveryChannelMessage {
|
type DiscoveryLoadingState = [isLoaded: boolean];
|
||||||
isLoaded: boolean;
|
|
||||||
|
function isExtensionDiscoveryChannelMessage(args: unknown[]): args is DiscoveryLoadingState {
|
||||||
|
return hasTypedProperty(args, 0, isBoolean)
|
||||||
|
&& args.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the lstat is for a directory-like file (e.g. isDirectory or symbolic link)
|
* Returns true if the lstat is for a directory-like file (e.g. isDirectory or symbolic link)
|
||||||
* @param lstat the stats to compare
|
* @param lstat the stats to compare
|
||||||
*/
|
*/
|
||||||
const isDirectoryLike = (lstat: fs.Stats) => lstat.isDirectory() || lstat.isSymbolicLink();
|
function isDirectoryLike(lstat: fs.Stats): boolean {
|
||||||
|
return lstat.isDirectory() || lstat.isSymbolicLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensionDiscoveryState = createTypedSender({
|
||||||
|
channel: "extension-discovery:state",
|
||||||
|
verifier: isExtensionDiscoveryChannelMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
function ExtensionDiscoveryInitState(): boolean {
|
||||||
|
return extensionDiscovery.isLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensionDiscoveryInitState = createTypedInvoker({
|
||||||
|
channel: "extension-discovery:init-state",
|
||||||
|
handler: ExtensionDiscoveryInitState,
|
||||||
|
verifier: isEmptyArgs,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discovers installed bundled and local extensions from the filesystem.
|
* Discovers installed bundled and local extensions from the filesystem.
|
||||||
@ -61,9 +82,6 @@ export class ExtensionDiscovery {
|
|||||||
@observable isLoaded = false;
|
@observable isLoaded = false;
|
||||||
whenLoaded = when(() => this.isLoaded);
|
whenLoaded = when(() => this.isLoaded);
|
||||||
|
|
||||||
// IPC channel to broadcast changes to extension-discovery from main
|
|
||||||
protected static readonly extensionDiscoveryChannel = "extension-discovery:main";
|
|
||||||
|
|
||||||
public events: EventEmitter;
|
public events: EventEmitter;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -95,29 +113,16 @@ export class ExtensionDiscovery {
|
|||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
await this.initRenderer();
|
extensionDiscoveryState.on((event, isLoaded) => {
|
||||||
} else {
|
|
||||||
await this.initMain();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async initRenderer() {
|
|
||||||
const onMessage = ({ isLoaded }: ExtensionDiscoveryChannelMessage) => {
|
|
||||||
this.isLoaded = isLoaded;
|
this.isLoaded = isLoaded;
|
||||||
};
|
});
|
||||||
|
|
||||||
requestMain(ExtensionDiscovery.extensionDiscoveryChannel).then(onMessage);
|
this.isLoaded = await extensionDiscoveryInitState.invoke();
|
||||||
subscribeToBroadcast(ExtensionDiscovery.extensionDiscoveryChannel, (_event, message: ExtensionDiscoveryChannelMessage) => {
|
} else {
|
||||||
onMessage(message);
|
reaction(() => this.toJSON(), loadingState => {
|
||||||
|
extensionDiscoveryState.broadcast(...loadingState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initMain() {
|
|
||||||
handleRequest(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON());
|
|
||||||
|
|
||||||
reaction(() => this.toJSON(), () => {
|
|
||||||
this.broadcast();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -450,17 +455,13 @@ export class ExtensionDiscovery {
|
|||||||
return this.getByManifest(manifestPath, { isBundled });
|
return this.getByManifest(manifestPath, { isBundled });
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON(): ExtensionDiscoveryChannelMessage {
|
toJSON(): DiscoveryLoadingState {
|
||||||
return toJS({
|
return toJS([
|
||||||
isLoaded: this.isLoaded
|
this.isLoaded
|
||||||
}, {
|
], {
|
||||||
recurseEverything: true
|
recurseEverything: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast() {
|
|
||||||
broadcastMessage(ExtensionDiscovery.extensionDiscoveryChannel, this.toJSON());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extensionDiscovery = new ExtensionDiscovery();
|
export const extensionDiscovery = new ExtensionDiscovery();
|
||||||
|
|||||||
@ -26,8 +26,8 @@ import type { LensExtensionId } from "../extensions/lens-extension";
|
|||||||
import { installDeveloperTools } from "./developer-tools";
|
import { installDeveloperTools } from "./developer-tools";
|
||||||
import { filesystemProvisionerStore } from "./extension-filesystem";
|
import { filesystemProvisionerStore } from "./extension-filesystem";
|
||||||
import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||||
import { bindBroadcastHandlers } from "../common/ipc";
|
|
||||||
import { startUpdateChecking } from "./app-updater";
|
import { startUpdateChecking } from "./app-updater";
|
||||||
|
import "../common/ipc"; // make sure that the handlers are registered
|
||||||
|
|
||||||
const workingDir = path.join(app.getPath("appData"), appName);
|
const workingDir = path.join(app.getPath("appData"), appName);
|
||||||
let proxyPort: number;
|
let proxyPort: number;
|
||||||
@ -66,8 +66,6 @@ app.on("ready", async () => {
|
|||||||
logger.info("🐚 Syncing shell environment");
|
logger.info("🐚 Syncing shell environment");
|
||||||
await shellSync();
|
await shellSync();
|
||||||
|
|
||||||
bindBroadcastHandlers();
|
|
||||||
|
|
||||||
powerMonitor.on("shutdown", () => {
|
powerMonitor.on("shutdown", () => {
|
||||||
app.exit();
|
app.exit();
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user