CH-J ToolsSerververwaltung über SSH
Menü
Veröffentlichte Quellen

CH-J Tools

Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.

Quellen als ZIP herunterladen
CH-J / Server Manager 406 Dateien
core-20261024T135205Z-7c487d719de2 Hinzugefügt: · Erstellt:
index.js Hinzugefügt: Datum nicht erfasst 16,3 KB · 352 Zeilen
Code Datei herunterladen
1"use strict";
3const path = require("node:path");
4const { app, BrowserWindow, clipboard, dialog, ipcMain, protocol, shell } = require("electron");
5const { createMainWindow, hardenSession } = require("./bootstrap/createMainWindow");
6const { ConfigStore } = require("./config/configStore");
7const { registerCoreIpc } = require("./ipc/registerCoreIpc");
8const { Logger } = require("./logging/logger");
9const { LogService } = require("./logging/logService");
10const { RemoteFileService } = require("./files/remoteFileService");
11const { TransferManager } = require("./files/transferManager");
12const { LocalHashService } = require("./hashing/localHashService");
13const { DiagnosticsService } = require("./diagnostics/diagnosticsService");
14const { PLUGIN_API_VERSION, PluginRegistry } = require("./plugins/pluginRegistry");
15const { PluginCatalogProvider } = require("./plugins/pluginCatalogProvider");
16const { PluginInstaller } = require("./plugins/pluginInstaller");
17const { PluginRuntime } = require("./plugins/pluginRuntime");
18const { PluginService } = require("./plugins/pluginService");
19const { KeyGeneratorService } = require("./keys/keyGeneratorService");
20const { ProfileService } = require("./profiles/profileService");
21const { createTestHttpsFetch } = require("./security/testHttpsFetch");
22const { VaultStore } = require("./security/vaultStore");
23const { BiometricService } = require("./security/biometrics/biometricService");
24const { PlatformAdapter } = require("./security/biometrics/platformAdapter");
25const { VaultLockController } = require("./security/vaultLockController");
26const { SessionManager } = require("./sessions/sessionManager");
27const { PrivilegedExecutor } = require("./sessions/privilegedExec");
28const { UserAdminService, createUserAdminApi } = require("./users/userAdminService");
29const { WindowsUserAdminService } = require("./users/windowsUserAdminService");
30const { WindowsExecutor } = require("./sessions/windowsPrivilegedExec");
31const { detectPlatform } = require("./sessions/platformDetect");
32const { VirtualMemoryService } = require("./system/virtualMemoryService");
33const { DiskService } = require("./disks/diskService");
34const { HashUrlProvider } = require("./updates/hashUrlProvider");
35const { OpenPgpVerifier } = require("./updates/openPgpVerifier");
36const { UpdateInstallerLauncher } = require("./updates/updateInstallerLauncher");
37const { UpdateService } = require("./updates/updateService");
38const buildInfo = require("../shared/buildInfo.json");
39const { LicenseAcceptance } = require("./legal/licenseAcceptance");
40const { showLicenseWindow } = require("./legal/showLicenseWindow");
42app.setName("CH-J Server Manager");
43const buildSmokeUserData = process.env.CHJ_BUILD_SMOKE_USER_DATA_DIR;
44if (buildSmokeUserData && path.isAbsolute(buildSmokeUserData)) app.setPath("userData", buildSmokeUserData);
45if (process.platform === "linux") {
46 app.setDesktopName("ch-j-server-manager.desktop");
47 app.commandLine.appendSwitch("class", "ch-j-server-manager");
49if (process.platform === "win32") app.setAppUserModelId("de.ch-j.servermanager");
50protocol.registerSchemesAsPrivileged([{ scheme: "chj-plugin", privileges: { standard: true, secure: true, supportFetchAPI: true } }]);
52let mainWindow = null;
53let logger = null;
54let updateService = null;
55let sessionManager = null;
56let vaultStore = null;
57let diagnosticsService = null;
58let biometricService = null;
59let vaultLockController = null;
60let transferManager = null;
61let licenseWindow = null;
62let coreReady = false;
64function watchWindow(window) {
65 window.webContents.on("before-input-event", () => vaultLockController?.activity());
66 window.webContents.on("before-mouse-event", () => vaultLockController?.activity());
67 window.on("blur", () => {
68 const checkFocus = () => {
69 if (BrowserWindow.getFocusedWindow()) return;
70 if (biometricService?.active) { setTimeout(checkFocus, 250).unref(); return; }
71 void vaultLockController?.lostFocus().catch(error => logger?.warn("Vault focus lock failed.", { message: error.message }));
72 };
73 setTimeout(checkFocus, 250).unref();
74 });
75 window.on("closed", () => { if (window === mainWindow) diagnosticsService?.stopAll(); });
78const hasSingleInstanceLock = buildSmokeUserData ? true : app.requestSingleInstanceLock();
79if (!hasSingleInstanceLock) app.quit();
81function focusMainWindow() {
82 if (licenseWindow && !licenseWindow.isDestroyed()) { licenseWindow.show(); licenseWindow.focus(); return; }
83 if (!mainWindow || mainWindow.isDestroyed()) return;
84 if (mainWindow.isMinimized()) mainWindow.restore();
85 mainWindow.show();
86 mainWindow.focus();
89async function bootstrap() {
90 const storageRoot = app.getPath("userData");
91 const appIcon = path.join(__dirname, "..", "..", "build", "icon.png");
92 logger = new Logger(storageRoot);
93 const logService = new LogService(logger.logPath);
94 const configStore = new ConfigStore(storageRoot);
95 configStore.load();
96 hardenSession(logger);
97 const acceptance = new LicenseAcceptance({
98 storageRoot,
99 licensePath: app.isPackaged ? path.join(process.resourcesPath, "licenses", "APPLICATION_LICENSE.txt") : path.join(app.getAppPath(), "LICENSE"),
100 docsRoot: path.join(app.isPackaged ? process.resourcesPath : app.getAppPath(), "docs"),
101 appVersion: app.getVersion(), buildId: buildInfo.buildId
102 });
103 if (!acceptance.isAccepted()) {
104 const accepted = await showLicenseWindow({ BrowserWindow, ipcMain, acceptance, language: configStore.get().ui.language,
105 onWindow: window => { licenseWindow = window; } });
106 if (!accepted) { app.quit(); return; }
107 }
108 diagnosticsService = new DiagnosticsService({ storageRoot });
109 const testUpdateFetch = createTestHttpsFetch({
110 allowedBaseUrls: configStore.get().updates.baseUrls,
111 // Jen starý host používá dočasnou alfa výjimku. Kanonický www host
112 // musí vždy projít standardním ověřením veřejného certifikátu.
113 insecureBaseUrls: ["https://sm.ch-j.de/"]
114 });
115 const pluginRegistry = new PluginRegistry(storageRoot, logger, { bundledRoot: path.join(__dirname, "firstPartyPlugins") });
116 vaultStore = new VaultStore(storageRoot);
117 const profileService = new ProfileService(vaultStore);
118 sessionManager = new SessionManager({ profileService, logger });
119 const privilegedExecutor = new PrivilegedExecutor({ sessionManager, logger });
120 // One shared sudo implementation: the editor/file helper uses the same executor as Users and swap.
121 sessionManager.privilegedExecutor = privilegedExecutor;
122 const windowsService = new WindowsUserAdminService({ sessionManager, windowsExecutor: new WindowsExecutor({ sessionManager, logger }), logger });
123 const virtualMemory = new VirtualMemoryService({ sessionManager, privilegedExecutor, logger });
124 const diskService = new DiskService({ sessionManager, privilegedExecutor, logger });
125 const userAdmin = createUserAdminApi(new UserAdminService({ sessionManager, privilegedExecutor, windowsService, detectPlatform, logger }));
126 const keyGeneratorService = new KeyGeneratorService({
127 writeClipboard: (text) => clipboard.writeText(text),
128 selectSavePath: async (suggestedName) => {
129 const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit pár SSH klíčů", defaultPath: suggestedName });
130 return { canceled: result.canceled, path: result.filePath || null };
131 }
132 });
133 const remoteFileService = new RemoteFileService({
134 sessionManager,
135 selectUploadPath: async () => {
136 const result = await dialog.showOpenDialog(mainWindow, { title: "Vybrat soubory nebo složky k nahrání", properties: ["openFile", "openDirectory", "multiSelections"] });
137 return { canceled: result.canceled, paths: result.filePaths };
138 },
139 selectDownloadPath: async (suggestedName) => {
140 const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit stažený soubor", defaultPath: suggestedName });
141 return { canceled: result.canceled, path: result.filePath || null };
142 },
143 selectDownloadDirectory: async () => {
144 const result = await dialog.showOpenDialog(mainWindow, { title: "Vybrat složku pro stažení", properties: ["openDirectory", "createDirectory"] });
145 return { canceled: result.canceled, path: result.filePaths[0] || null };
146 },
147 selectArchivePath: async (suggestedName, format) => {
148 const extensions = format === "tar.gz" ? ["tar.gz", "tgz"] : [format];
149 const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit vzdálené položky jako archiv", defaultPath: suggestedName, filters: [{ name: format.toUpperCase(), extensions }] });
150 return { canceled: result.canceled, path: result.filePath || null };
151 },
152 privilegedExecutor,
153 logger
154 });
155 transferManager = new TransferManager({ sessionManager, remoteFileService, storageRoot, logger });
156 const hashText = (key) => require("./firstPartyPlugins/chj.hash-checksum/0.0.1/ui/i18n").createI18n(configStore.get().ui.language).t(key);
157 const localHashService = new LocalHashService({
158 selectFilesDialog: async ({ multiple }) => {
159 const result = await dialog.showOpenDialog(mainWindow, { title: hashText(multiple ? "Select local files to hash" : "Select a local file to hash"), properties: multiple ? ["openFile", "multiSelections"] : ["openFile"] });
160 return { canceled: result.canceled, paths: result.filePaths };
161 },
162 selectDirectoryDialog: async () => {
163 const result = await dialog.showOpenDialog(mainWindow, { title: hashText("Select a local directory"), properties: ["openDirectory"] });
164 return { canceled: result.canceled, path: result.filePaths[0] || null };
165 },
166 selectManifestDialog: async () => {
167 const result = await dialog.showOpenDialog(mainWindow, { title: hashText("Select a checksum manifest"), properties: ["openFile"], filters: [{ name: hashText("Checksum manifests"), extensions: ["sha224", "sha256", "sha384", "sha512", "sha3", "blake3", "md5", "sha1", "sfv", "checksums", "txt"] }] });
168 return { canceled: result.canceled, path: result.filePaths[0] || null };
169 },
170 selectSaveDialog: async ({ suggestedName }) => {
171 const result = await dialog.showSaveDialog(mainWindow, { title: hashText("Save checksum manifest"), defaultPath: suggestedName });
172 return { canceled: result.canceled, path: result.filePath || null };
173 },
174 writeClipboard: (text) => clipboard.writeText(text),
175 logger
176 });
177 const pluginInstaller = new PluginInstaller({ storageRoot, appVersion: app.getVersion(), pluginApiVersion: PLUGIN_API_VERSION, logger });
178 const pluginRuntime = new PluginRuntime({
179 BrowserWindow,
180 registry: pluginRegistry,
181 sessionManager,
182 keyGeneratorService,
183 logService,
184 remoteFileService,
185 transferManager,
186 localHashService,
187 userAdmin,
188 virtualMemory,
189 diskService,
190 getLanguage: () => configStore.get().ui.language,
191 getMainWindow: () => mainWindow,
192 isVaultUnlocked: () => vaultStore.status().unlocked,
193 preload: path.join(__dirname, "..", "preload", "pluginPreload.js"),
194 icon: appIcon,
195 onWindowState: (windows) => {
196 if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("core:pluginWindowState", windows);
197 },
198 logger
199 });
200 protocol.handle("chj-plugin", (request) => pluginRuntime.handleRequest(request));
201 pluginRuntime.registerIpc(ipcMain);
202 const pluginService = new PluginService({
203 appVersion: app.getVersion(),
204 pluginApiVersion: PLUGIN_API_VERSION,
205 configStore,
206 registry: pluginRegistry,
207 installer: pluginInstaller,
208 runtime: pluginRuntime,
209 logger,
210 providerFactory: (updateConfig) => new PluginCatalogProvider({
211 baseUrls: updateConfig.baseUrls,
212 downloadRoot: path.join(storageRoot, "plugins", ".downloads"),
213 fetchImpl: testUpdateFetch,
214 logger
215 })
216 });
217 const signingPublicKeyPath = app.isPackaged
218 ? path.join(process.resourcesPath, "signing", "ch-j-signing-public.asc")
219 : path.join(app.getAppPath(), "ch-j-signing-public.asc");
220 const signatureVerifier = new OpenPgpVerifier({
221 publicKeyPath: signingPublicKeyPath,
222 logger
223 });
224 updateService = new UpdateService({
225 appVersion: app.getVersion(),
226 buildId: buildInfo.buildId,
227 platform: process.platform,
228 arch: process.arch,
229 configStore,
230 logger,
231 providerFactory: (updateConfig) => new HashUrlProvider({
232 baseUrls: updateConfig.baseUrls,
233 downloadRoot: path.join(storageRoot, "updates", "downloads"),
234 fetchImpl: testUpdateFetch,
235 signatureVerifier,
236 logger
237 })
238 });
239 const updateInstallerLauncher = new UpdateInstallerLauncher({
240 platform: process.platform,
241 shell,
242 logger
243 });
245 for (const [cache, service] of [["Core update", updateService], ["plugin package", pluginService]]) {
246 try {
247 const result = service.cleanupDownloads();
248 if (result.cleaned) logger.info(`${cache} download cache cleaned on startup.`, { root: result.root });
249 } catch (error) {
250 logger.warn(`${cache} download cache cleanup failed.`, { message: error?.message || String(error) });
251 }
252 }
254 mainWindow = createMainWindow({ logger, icon: appIcon });
255 biometricService = new BiometricService({ storageRoot, vaultStore, configStore, adapter: new PlatformAdapter({
256 resourcesPath: process.resourcesPath, appPath: app.getAppPath(), isPackaged: app.isPackaged, getMainWindow: () => mainWindow
257 }) });
258 vaultLockController = new VaultLockController({ vaultStore, configStore, biometricService, sessionManager, pluginRuntime, diagnosticsService });
259 app.on("browser-window-created", (_event, window) => watchWindow(window));
260 watchWindow(mainWindow);
261 vaultLockController.on("locked", payload => {
262 if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("vault:locked", payload);
263 });
264 registerCoreIpc({
265 ipcMain,
266 clipboard,
267 shell,
268 dialog,
269 app,
270 configStore,
271 pluginRegistry,
272 pluginApiVersion: PLUGIN_API_VERSION,
273 pluginService,
274 pluginRuntime,
275 profileService,
276 sessionManager,
277 vaultStore,
278 biometricService,
279 vaultLockController,
280 updateService,
281 updateInstallerLauncher,
282 diagnosticsService,
283 getMainWindow: () => mainWindow,
284 logger
285 });
287 const sendToRenderer = (channel, payload) => {
288 if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send(channel, payload);
289 };
290 sessionManager.on("data", (payload) => sendToRenderer("ssh:data", payload));
291 sessionManager.on("state", (payload) => sendToRenderer("ssh:state", payload));
292 sessionManager.on("sessionError", (payload) => sendToRenderer("ssh:error", payload));
293 diagnosticsService.on("progress", (payload) => sendToRenderer("diagnostics:progress", payload));
295 mainWindow.webContents.once("did-finish-load", () => {
296 const config = configStore.get();
297 if (!config.updates.autoCheck) return;
298 updateService.check()
299 .then((state) => {
300 if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("core:updateState", state);
301 })
302 .catch((error) => logger.warn("Automatic update check failed.", { message: error?.message || String(error) }));
303 });
305 logger.info("Core initialized.", {
306 version: app.getVersion(),
307 buildId: buildInfo.buildId,
308 platform: process.platform,
309 arch: process.arch,
310 storageRoot
311 });
312 coreReady = true;
313 if (buildSmokeUserData) {
314 console.log("CHJ_BUILD_SMOKE_READY");
315 setTimeout(() => app.quit(), 50);
316 }
319app.on("second-instance", focusMainWindow);
320app.whenReady().then(bootstrap).catch((error) => {
321 logger?.error("Core bootstrap failed.", { message: error?.message || String(error), stack: error?.stack });
322 dialog.showErrorBox("CH-J Server Manager", error?.message || String(error));
323 app.quit();
324});
326app.on("activate", () => {
327 if (!coreReady) { focusMainWindow(); return; }
328 if (BrowserWindow.getAllWindows().length === 0) {
329 mainWindow = createMainWindow({ logger, icon: path.join(__dirname, "..", "..", "build", "icon.png") });
330 } else {
331 focusMainWindow();
332 }
333});
335app.on("window-all-closed", () => {
336 if (coreReady && process.platform !== "darwin") app.quit();
337});
339app.on("before-quit", () => {
340 transferManager?.shutdown();
341 vaultLockController?.dispose();
342 diagnosticsService?.stopAll();
343 void sessionManager?.disconnectAll("app-quit");
344 vaultStore?.lock();
345});
347process.on("uncaughtException", (error) => {
348 logger?.error("Uncaught exception.", { message: error?.message || String(error), stack: error?.stack });
349});
350process.on("unhandledRejection", (error) => {
351 logger?.error("Unhandled rejection.", { message: error?.message || String(error), stack: error?.stack });
352});
SHA-25651959517a93d2773b631c0d8912c556d56351d8a68cb6cf800c59b7b980ce772

CH-J Proprietary Software License 1.14 · Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.

SHA-256 des Archivs476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6
Dateien