公開済みのソース
ソースの ZIP をダウンロード
CH-J Server Manager
特定のアプリケーションリリースのディレクトリとファイルを閲覧できます。
CH-J / Server Manager
377 ファイル
core-20261019T111616Z-160a00bfcddc
追加日時: · ビルド日時:
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 { HashUrlProvider } = require("./updates/hashUrlProvider");34
const { OpenPgpVerifier } = require("./updates/openPgpVerifier");35
const { UpdateInstallerLauncher } = require("./updates/updateInstallerLauncher");36
const { UpdateService } = require("./updates/updateService");37
const buildInfo = require("../shared/buildInfo.json");38
const { LicenseAcceptance } = require("./legal/licenseAcceptance");39
const { showLicenseWindow } = require("./legal/showLicenseWindow");41
app.setName("CH-J Server Manager");42
const buildSmokeUserData = process.env.CHJ_BUILD_SMOKE_USER_DATA_DIR;43
if (buildSmokeUserData && path.isAbsolute(buildSmokeUserData)) app.setPath("userData", buildSmokeUserData);44
if (process.platform === "linux") {45
app.setDesktopName("ch-j-server-manager.desktop");46
app.commandLine.appendSwitch("class", "ch-j-server-manager");47
}48
if (process.platform === "win32") app.setAppUserModelId("de.ch-j.servermanager");49
protocol.registerSchemesAsPrivileged([{ scheme: "chj-plugin", privileges: { standard: true, secure: true, supportFetchAPI: true } }]);51
let mainWindow = null;52
let logger = null;53
let updateService = null;54
let sessionManager = null;55
let vaultStore = null;56
let diagnosticsService = null;57
let biometricService = null;58
let vaultLockController = null;59
let transferManager = null;60
let licenseWindow = null;61
let coreReady = false;63
function watchWindow(window) {64
window.webContents.on("before-input-event", () => vaultLockController?.activity());65
window.webContents.on("before-mouse-event", () => vaultLockController?.activity());66
window.on("blur", () => {67
const checkFocus = () => {68
if (BrowserWindow.getFocusedWindow()) return;69
if (biometricService?.active) { setTimeout(checkFocus, 250).unref(); return; }70
void vaultLockController?.lostFocus().catch(error => logger?.warn("Vault focus lock failed.", { message: error.message }));71
};72
setTimeout(checkFocus, 250).unref();73
});74
window.on("closed", () => { if (window === mainWindow) diagnosticsService?.stopAll(); });75
}77
const hasSingleInstanceLock = buildSmokeUserData ? true : app.requestSingleInstanceLock();78
if (!hasSingleInstanceLock) app.quit();80
function focusMainWindow() {81
if (licenseWindow && !licenseWindow.isDestroyed()) { licenseWindow.show(); licenseWindow.focus(); return; }82
if (!mainWindow || mainWindow.isDestroyed()) return;83
if (mainWindow.isMinimized()) mainWindow.restore();84
mainWindow.show();85
mainWindow.focus();86
}88
async function bootstrap() {89
const storageRoot = app.getPath("userData");90
const appIcon = path.join(__dirname, "..", "..", "build", "icon.png");91
logger = new Logger(storageRoot);92
const logService = new LogService(logger.logPath);93
const configStore = new ConfigStore(storageRoot);94
configStore.load();95
hardenSession(logger);96
const acceptance = new LicenseAcceptance({97
storageRoot,98
licensePath: app.isPackaged ? path.join(process.resourcesPath, "licenses", "APPLICATION_LICENSE.txt") : path.join(app.getAppPath(), "LICENSE"),99
docsRoot: path.join(app.isPackaged ? process.resourcesPath : app.getAppPath(), "docs"),100
appVersion: app.getVersion(), buildId: buildInfo.buildId101
});102
if (!acceptance.isAccepted()) {103
const accepted = await showLicenseWindow({ BrowserWindow, ipcMain, acceptance, language: configStore.get().ui.language,104
onWindow: window => { licenseWindow = window; } });105
if (!accepted) { app.quit(); return; }106
}107
diagnosticsService = new DiagnosticsService({ storageRoot });108
const testUpdateFetch = createTestHttpsFetch({109
allowedBaseUrls: configStore.get().updates.baseUrls,110
// Jen starý host používá dočasnou alfa výjimku. Kanonický www host111
// musí vždy projít standardním ověřením veřejného certifikátu.112
insecureBaseUrls: ["https://sm.ch-j.de/"]113
});114
const pluginRegistry = new PluginRegistry(storageRoot, logger, { bundledRoot: path.join(__dirname, "firstPartyPlugins") });115
vaultStore = new VaultStore(storageRoot);116
const profileService = new ProfileService(vaultStore);117
sessionManager = new SessionManager({ profileService, logger });118
const privilegedExecutor = new PrivilegedExecutor({ sessionManager, logger });119
// One shared sudo implementation: the editor/file helper uses the same executor as Users and swap.120
sessionManager.privilegedExecutor = privilegedExecutor;121
const windowsService = new WindowsUserAdminService({ sessionManager, windowsExecutor: new WindowsExecutor({ sessionManager, logger }), logger });122
const virtualMemory = new VirtualMemoryService({ sessionManager, privilegedExecutor, logger });123
const userAdmin = createUserAdminApi(new UserAdminService({ sessionManager, privilegedExecutor, windowsService, detectPlatform, logger }));124
const keyGeneratorService = new KeyGeneratorService({125
selectSavePath: async (suggestedName) => {126
const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit pár SSH klíčů", defaultPath: suggestedName });127
return { canceled: result.canceled, path: result.filePath || null };128
}129
});130
const remoteFileService = new RemoteFileService({131
sessionManager,132
selectUploadPath: async () => {133
const result = await dialog.showOpenDialog(mainWindow, { title: "Vybrat soubory nebo složky k nahrání", properties: ["openFile", "openDirectory", "multiSelections"] });134
return { canceled: result.canceled, paths: result.filePaths };135
},136
selectDownloadPath: async (suggestedName) => {137
const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit stažený soubor", defaultPath: suggestedName });138
return { canceled: result.canceled, path: result.filePath || null };139
},140
selectDownloadDirectory: async () => {141
const result = await dialog.showOpenDialog(mainWindow, { title: "Vybrat složku pro stažení", properties: ["openDirectory", "createDirectory"] });142
return { canceled: result.canceled, path: result.filePaths[0] || null };143
},144
selectArchivePath: async (suggestedName, format) => {145
const extensions = format === "tar.gz" ? ["tar.gz", "tgz"] : [format];146
const result = await dialog.showSaveDialog(mainWindow, { title: "Uložit vzdálené položky jako archiv", defaultPath: suggestedName, filters: [{ name: format.toUpperCase(), extensions }] });147
return { canceled: result.canceled, path: result.filePath || null };148
},149
privilegedExecutor,150
logger151
});152
transferManager = new TransferManager({ sessionManager, remoteFileService, storageRoot, logger });153
const hashText = (key) => require("./firstPartyPlugins/chj.hash-checksum/0.0.1/ui/i18n").createI18n(configStore.get().ui.language).t(key);154
const localHashService = new LocalHashService({155
selectFilesDialog: async ({ multiple }) => {156
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"] });157
return { canceled: result.canceled, paths: result.filePaths };158
},159
selectDirectoryDialog: async () => {160
const result = await dialog.showOpenDialog(mainWindow, { title: hashText("Select a local directory"), properties: ["openDirectory"] });161
return { canceled: result.canceled, path: result.filePaths[0] || null };162
},163
selectManifestDialog: async () => {164
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"] }] });165
return { canceled: result.canceled, path: result.filePaths[0] || null };166
},167
selectSaveDialog: async ({ suggestedName }) => {168
const result = await dialog.showSaveDialog(mainWindow, { title: hashText("Save checksum manifest"), defaultPath: suggestedName });169
return { canceled: result.canceled, path: result.filePath || null };170
},171
writeClipboard: (text) => clipboard.writeText(text),172
logger173
});174
const pluginInstaller = new PluginInstaller({ storageRoot, appVersion: app.getVersion(), pluginApiVersion: PLUGIN_API_VERSION, logger });175
const pluginRuntime = new PluginRuntime({176
BrowserWindow,177
registry: pluginRegistry,178
sessionManager,179
keyGeneratorService,180
logService,181
remoteFileService,182
transferManager,183
localHashService,184
userAdmin,185
virtualMemory,186
getLanguage: () => configStore.get().ui.language,187
getMainWindow: () => mainWindow,188
isVaultUnlocked: () => vaultStore.status().unlocked,189
preload: path.join(__dirname, "..", "preload", "pluginPreload.js"),190
icon: appIcon,191
onWindowState: (windows) => {192
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("core:pluginWindowState", windows);193
},194
logger195
});196
protocol.handle("chj-plugin", (request) => pluginRuntime.handleRequest(request));197
pluginRuntime.registerIpc(ipcMain);198
const pluginService = new PluginService({199
appVersion: app.getVersion(),200
pluginApiVersion: PLUGIN_API_VERSION,201
configStore,202
registry: pluginRegistry,203
installer: pluginInstaller,204
runtime: pluginRuntime,205
logger,206
providerFactory: (updateConfig) => new PluginCatalogProvider({207
baseUrls: updateConfig.baseUrls,208
downloadRoot: path.join(storageRoot, "plugins", ".downloads"),209
fetchImpl: testUpdateFetch,210
logger211
})212
});213
const signingPublicKeyPath = app.isPackaged214
? path.join(process.resourcesPath, "signing", "ch-j-signing-public.asc")215
: path.join(app.getAppPath(), "ch-j-signing-public.asc");216
const signatureVerifier = new OpenPgpVerifier({217
publicKeyPath: signingPublicKeyPath,218
logger219
});220
updateService = new UpdateService({221
appVersion: app.getVersion(),222
buildId: buildInfo.buildId,223
platform: process.platform,224
arch: process.arch,225
configStore,226
logger,227
providerFactory: (updateConfig) => new HashUrlProvider({228
baseUrls: updateConfig.baseUrls,229
downloadRoot: path.join(storageRoot, "updates", "downloads"),230
fetchImpl: testUpdateFetch,231
signatureVerifier,232
logger233
})234
});235
const updateInstallerLauncher = new UpdateInstallerLauncher({236
platform: process.platform,237
shell,238
logger239
});241
for (const [cache, service] of [["Core update", updateService], ["plugin package", pluginService]]) {242
try {243
const result = service.cleanupDownloads();244
if (result.cleaned) logger.info(`${cache} download cache cleaned on startup.`, { root: result.root });245
} catch (error) {246
logger.warn(`${cache} download cache cleanup failed.`, { message: error?.message || String(error) });247
}248
}250
mainWindow = createMainWindow({ logger, icon: appIcon });251
biometricService = new BiometricService({ storageRoot, vaultStore, configStore, adapter: new PlatformAdapter({252
resourcesPath: process.resourcesPath, appPath: app.getAppPath(), isPackaged: app.isPackaged, getMainWindow: () => mainWindow253
}) });254
vaultLockController = new VaultLockController({ vaultStore, configStore, biometricService, sessionManager, pluginRuntime, diagnosticsService });255
app.on("browser-window-created", (_event, window) => watchWindow(window));256
watchWindow(mainWindow);257
vaultLockController.on("locked", payload => {258
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("vault:locked", payload);259
});260
registerCoreIpc({261
ipcMain,262
clipboard,263
shell,264
dialog,265
app,266
configStore,267
pluginRegistry,268
pluginApiVersion: PLUGIN_API_VERSION,269
pluginService,270
pluginRuntime,271
profileService,272
sessionManager,273
vaultStore,274
biometricService,275
vaultLockController,276
updateService,277
updateInstallerLauncher,278
diagnosticsService,279
getMainWindow: () => mainWindow,280
logger281
});283
const sendToRenderer = (channel, payload) => {284
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send(channel, payload);285
};286
sessionManager.on("data", (payload) => sendToRenderer("ssh:data", payload));287
sessionManager.on("state", (payload) => sendToRenderer("ssh:state", payload));288
sessionManager.on("sessionError", (payload) => sendToRenderer("ssh:error", payload));289
diagnosticsService.on("progress", (payload) => sendToRenderer("diagnostics:progress", payload));291
mainWindow.webContents.once("did-finish-load", () => {292
const config = configStore.get();293
if (!config.updates.autoCheck) return;294
updateService.check()295
.then((state) => {296
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("core:updateState", state);297
})298
.catch((error) => logger.warn("Automatic update check failed.", { message: error?.message || String(error) }));299
});301
logger.info("Core initialized.", {302
version: app.getVersion(),303
buildId: buildInfo.buildId,304
platform: process.platform,305
arch: process.arch,306
storageRoot307
});308
coreReady = true;309
if (buildSmokeUserData) {310
console.log("CHJ_BUILD_SMOKE_READY");311
setTimeout(() => app.quit(), 50);312
}313
}315
app.on("second-instance", focusMainWindow);316
app.whenReady().then(bootstrap).catch((error) => {317
logger?.error("Core bootstrap failed.", { message: error?.message || String(error), stack: error?.stack });318
dialog.showErrorBox("CH-J Server Manager", error?.message || String(error));319
app.quit();320
});322
app.on("activate", () => {323
if (!coreReady) { focusMainWindow(); return; }324
if (BrowserWindow.getAllWindows().length === 0) {325
mainWindow = createMainWindow({ logger, icon: path.join(__dirname, "..", "..", "build", "icon.png") });326
} else {327
focusMainWindow();328
}329
});331
app.on("window-all-closed", () => {332
if (coreReady && process.platform !== "darwin") app.quit();333
});335
app.on("before-quit", () => {336
transferManager?.shutdown();337
vaultLockController?.dispose();338
diagnosticsService?.stopAll();339
void sessionManager?.disconnectAll("app-quit");340
vaultStore?.lock();341
});343
process.on("uncaughtException", (error) => {344
logger?.error("Uncaught exception.", { message: error?.message || String(error), stack: error?.stack });345
});346
process.on("unhandledRejection", (error) => {347
logger?.error("Unhandled rejection.", { message: error?.message || String(error), stack: error?.stack });348
});SHA-256
f7449303d88c33c345397553c327acf6a05df1d046413c653c4022ef13526b72CH-J Proprietary Software License 1.14 · ソースは CH-J Proprietary Software License 1.14 に基づいて提供されています。公開されていても、ライセンス条項が変わったり、追加の権利が付与されたりすることはありません。
アーカイブの SHA-256
a9605c0870a223b6a8542bb0705edd51d8b04f3069582653417a8397fabcc09a