CH-J Server ManagerSSH کے ذریعے سرور کا انتظام
مینو
شائع شدہ سورس

CH-J Server Manager

ایپلیکیشن کی کسی مخصوص ریلیز کی ڈائریکٹریز اور فائلیں دیکھیں۔

سورس ZIP ڈاؤن لوڈ کریں
CH-J / Server Manager 377 فائلیں
core-20261019T111616Z-160a00bfcddc شامل کیا گیا: · بلڈ کیا گیا:
index.js شامل کیا گیا: تاریخ درج نہیں 16.1 KB · 348 لائنیں
کوڈ فائل ڈاؤن لوڈ کریں
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 { HashUrlProvider } = require("./updates/hashUrlProvider");
34const { OpenPgpVerifier } = require("./updates/openPgpVerifier");
35const { UpdateInstallerLauncher } = require("./updates/updateInstallerLauncher");
36const { UpdateService } = require("./updates/updateService");
37const buildInfo = require("../shared/buildInfo.json");
38const { LicenseAcceptance } = require("./legal/licenseAcceptance");
39const { showLicenseWindow } = require("./legal/showLicenseWindow");
41app.setName("CH-J Server Manager");
42const buildSmokeUserData = process.env.CHJ_BUILD_SMOKE_USER_DATA_DIR;
43if (buildSmokeUserData && path.isAbsolute(buildSmokeUserData)) app.setPath("userData", buildSmokeUserData);
44if (process.platform === "linux") {
45 app.setDesktopName("ch-j-server-manager.desktop");
46 app.commandLine.appendSwitch("class", "ch-j-server-manager");
48if (process.platform === "win32") app.setAppUserModelId("de.ch-j.servermanager");
49protocol.registerSchemesAsPrivileged([{ scheme: "chj-plugin", privileges: { standard: true, secure: true, supportFetchAPI: true } }]);
51let mainWindow = null;
52let logger = null;
53let updateService = null;
54let sessionManager = null;
55let vaultStore = null;
56let diagnosticsService = null;
57let biometricService = null;
58let vaultLockController = null;
59let transferManager = null;
60let licenseWindow = null;
61let coreReady = false;
63function 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(); });
77const hasSingleInstanceLock = buildSmokeUserData ? true : app.requestSingleInstanceLock();
78if (!hasSingleInstanceLock) app.quit();
80function 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();
88async 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.buildId
101 });
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 host
111 // 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 logger
151 });
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 logger
173 });
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 logger
195 });
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 logger
211 })
212 });
213 const signingPublicKeyPath = app.isPackaged
214 ? 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 logger
219 });
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 logger
233 })
234 });
235 const updateInstallerLauncher = new UpdateInstallerLauncher({
236 platform: process.platform,
237 shell,
238 logger
239 });
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: () => mainWindow
253 }) });
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 logger
281 });
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 storageRoot
307 });
308 coreReady = true;
309 if (buildSmokeUserData) {
310 console.log("CHJ_BUILD_SMOKE_READY");
311 setTimeout(() => app.quit(), 50);
312 }
315app.on("second-instance", focusMainWindow);
316app.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});
322app.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});
331app.on("window-all-closed", () => {
332 if (coreReady && process.platform !== "darwin") app.quit();
333});
335app.on("before-quit", () => {
336 transferManager?.shutdown();
337 vaultLockController?.dispose();
338 diagnosticsService?.stopAll();
339 void sessionManager?.disconnectAll("app-quit");
340 vaultStore?.lock();
341});
343process.on("uncaughtException", (error) => {
344 logger?.error("Uncaught exception.", { message: error?.message || String(error), stack: error?.stack });
345});
346process.on("unhandledRejection", (error) => {
347 logger?.error("Unhandled rejection.", { message: error?.message || String(error), stack: error?.stack });
348});
SHA-256f7449303d88c33c345397553c327acf6a05df1d046413c653c4022ef13526b72

CH-J Proprietary Software License 1.14 · سورس CH-J Proprietary Software License 1.14 کے تحت فراہم کیا جاتا ہے۔ اس کی دستیابی لائسنس کی شرائط نہیں بدلتی اور نہ ہی اضافی حقوق دیتی ہے۔

آرکائیو کا SHA-256a9605c0870a223b6a8542bb0705edd51d8b04f3069582653417a8397fabcc09a
فائلیں