Vydané zdroje
Stáhnout zdroje ZIP
CH-J Tools
Procházejte adresáře a soubory konkrétního vydání aplikace.
CH-J / Server Manager
406 souborů
core-20261024T135205Z-7c487d719de2
Přidáno: · Sestaveno:
1
"use strict";3
const path = require("node:path");4
const { app, BrowserWindow, clipboard, dialog, ipcMain, protocol, shell } = require("electron");5
const { createMainWindow, hardenSession } = require("./bootstrap/createMainWindow");6
const { ConfigStore } = require("./config/configStore");7
const { registerCoreIpc } = require("./ipc/registerCoreIpc");8
const { Logger } = require("./logging/logger");9
const { LogService } = require("./logging/logService");10
const { RemoteFileService } = require("./files/remoteFileService");11
const { TransferManager } = require("./files/transferManager");12
const { LocalHashService } = require("./hashing/localHashService");13
const { DiagnosticsService } = require("./diagnostics/diagnosticsService");14
const { PLUGIN_API_VERSION, PluginRegistry } = require("./plugins/pluginRegistry");15
const { PluginCatalogProvider } = require("./plugins/pluginCatalogProvider");16
const { PluginInstaller } = require("./plugins/pluginInstaller");17
const { PluginRuntime } = require("./plugins/pluginRuntime");18
const { PluginService } = require("./plugins/pluginService");19
const { KeyGeneratorService } = require("./keys/keyGeneratorService");20
const { ProfileService } = require("./profiles/profileService");21
const { createTestHttpsFetch } = require("./security/testHttpsFetch");22
const { VaultStore } = require("./security/vaultStore");23
const { BiometricService } = require("./security/biometrics/biometricService");24
const { PlatformAdapter } = require("./security/biometrics/platformAdapter");25
const { VaultLockController } = require("./security/vaultLockController");26
const { SessionManager } = require("./sessions/sessionManager");27
const { PrivilegedExecutor } = require("./sessions/privilegedExec");28
const { UserAdminService, createUserAdminApi } = require("./users/userAdminService");29
const { WindowsUserAdminService } = require("./users/windowsUserAdminService");30
const { WindowsExecutor } = require("./sessions/windowsPrivilegedExec");31
const { detectPlatform } = require("./sessions/platformDetect");32
const { VirtualMemoryService } = require("./system/virtualMemoryService");33
const { DiskService } = require("./disks/diskService");34
const { HashUrlProvider } = require("./updates/hashUrlProvider");35
const { OpenPgpVerifier } = require("./updates/openPgpVerifier");36
const { UpdateInstallerLauncher } = require("./updates/updateInstallerLauncher");37
const { UpdateService } = require("./updates/updateService");38
const buildInfo = require("../shared/buildInfo.json");39
const { LicenseAcceptance } = require("./legal/licenseAcceptance");40
const { showLicenseWindow } = require("./legal/showLicenseWindow");42
app.setName("CH-J Server Manager");43
const buildSmokeUserData = process.env.CHJ_BUILD_SMOKE_USER_DATA_DIR;44
if (buildSmokeUserData && path.isAbsolute(buildSmokeUserData)) app.setPath("userData", buildSmokeUserData);45
if (process.platform === "linux") {46
app.setDesktopName("ch-j-server-manager.desktop");47
app.commandLine.appendSwitch("class", "ch-j-server-manager");48
}49
if (process.platform === "win32") app.setAppUserModelId("de.ch-j.servermanager");50
protocol.registerSchemesAsPrivileged([{ scheme: "chj-plugin", privileges: { standard: true, secure: true, supportFetchAPI: true } }]);52
let mainWindow = null;53
let logger = null;54
let updateService = null;55
let sessionManager = null;56
let vaultStore = null;57
let diagnosticsService = null;58
let biometricService = null;59
let vaultLockController = null;60
let transferManager = null;61
let licenseWindow = null;62
let coreReady = false;64
function 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(); });76
}78
const hasSingleInstanceLock = buildSmokeUserData ? true : app.requestSingleInstanceLock();79
if (!hasSingleInstanceLock) app.quit();81
function 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();87
}89
async 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.buildId102
});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 host112
// 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
logger154
});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
logger176
});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
logger199
});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
logger215
})216
});217
const signingPublicKeyPath = app.isPackaged218
? 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
logger223
});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
logger237
})238
});239
const updateInstallerLauncher = new UpdateInstallerLauncher({240
platform: process.platform,241
shell,242
logger243
});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: () => mainWindow257
}) });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
logger285
});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
storageRoot311
});312
coreReady = true;313
if (buildSmokeUserData) {314
console.log("CHJ_BUILD_SMOKE_READY");315
setTimeout(() => app.quit(), 50);316
}317
}319
app.on("second-instance", focusMainWindow);320
app.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
});326
app.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
});335
app.on("window-all-closed", () => {336
if (coreReady && process.platform !== "darwin") app.quit();337
});339
app.on("before-quit", () => {340
transferManager?.shutdown();341
vaultLockController?.dispose();342
diagnosticsService?.stopAll();343
void sessionManager?.disconnectAll("app-quit");344
vaultStore?.lock();345
});347
process.on("uncaughtException", (error) => {348
logger?.error("Uncaught exception.", { message: error?.message || String(error), stack: error?.stack });349
});350
process.on("unhandledRejection", (error) => {351
logger?.error("Unhandled rejection.", { message: error?.message || String(error), stack: error?.stack });352
});SHA-256
51959517a93d2773b631c0d8912c556d56351d8a68cb6cf800c59b7b980ce772CH-J Proprietary Software License 1.14 · Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.
SHA-256 archivu
476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6