CH-J Server Managerspráva serverů přes SSH
Menu
Vydané zdroje

CH-J Server Manager

Procházejte adresáře a soubory konkrétního vydání aplikace.

Stáhnout zdroje ZIP
CH-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í.

4,4 KB · 145 řádkůStáhnout soubor
1"use strict";
3const fs = require("node:fs");
4const path = require("node:path");
5const { atomicWriteJson } = require("../storage/atomicFile");
7const SUPPORTED_LANGUAGES = Object.freeze(["cs", "de", "en"]);
8const CATALOG_CHANNELS = Object.freeze(["alpha", "beta", "stable", "all"]);
10const DEFAULT_CONFIG = Object.freeze({
11 schemaVersion: 1,
12 ui: {
13 language: "cs",
14 theme: "system"
15 },
16 updates: {
17 channel: "alpha",
18 autoCheck: true,
19 lastLaunchedRelease: null,
20 baseUrls: [
21 "https://www.sm.ch-j.de/",
22 "https://sm.ch-j.de/"
23 ]
24 },
25 plugins: {
26 channel: "alpha"
27 },
28 security: { autoLockMinutes: 0, lockOnBlur: false, requireSystemAuthentication: false }
29});
31function clone(value) {
32 return JSON.parse(JSON.stringify(value));
35function normalizeReleaseMarker(value) {
36 if (!value || typeof value !== "object" || Array.isArray(value)) return null;
37 const id = String(value.id || "").trim();
38 const version = String(value.version || "").trim();
39 const publishedAt = String(value.publishedAt || "").trim();
40 const sha512 = String(value.sha512 || "").trim().toLowerCase();
41 if (!id || !version || Number.isNaN(Date.parse(publishedAt)) || !/^[a-f0-9]{128}$/.test(sha512)) return null;
42 return { id, version, publishedAt, sha512 };
45function normalizeConfig(input = {}) {
46 const source = input && typeof input === "object" && !Array.isArray(input) ? input : {};
47 const channel = CATALOG_CHANNELS.includes(source.updates?.channel)
48 ? source.updates.channel
49 : DEFAULT_CONFIG.updates.channel;
50 const pluginChannel = CATALOG_CHANNELS.includes(source.plugins?.channel)
51 ? source.plugins.channel
52 : DEFAULT_CONFIG.plugins.channel;
53 const theme = ["system", "dark", "light"].includes(source.ui?.theme)
54 ? source.ui.theme
55 : DEFAULT_CONFIG.ui.theme;
56 const language = SUPPORTED_LANGUAGES.includes(String(source.ui?.language || ""))
57 ? String(source.ui.language)
58 : DEFAULT_CONFIG.ui.language;
60 return {
61 schemaVersion: 1,
62 ui: { language, theme },
63 updates: {
64 channel,
65 autoCheck: source.updates?.autoCheck !== false,
66 lastLaunchedRelease: normalizeReleaseMarker(source.updates?.lastLaunchedRelease),
67 baseUrls: clone(DEFAULT_CONFIG.updates.baseUrls)
68 },
69 plugins: { channel: pluginChannel },
70 security: {
71 autoLockMinutes: [0, 1, 5, 10, 15, 30, 60].includes(source.security?.autoLockMinutes) ? source.security.autoLockMinutes : 0,
72 lockOnBlur: source.security?.lockOnBlur === true,
73 requireSystemAuthentication: source.security?.requireSystemAuthentication === true
74 }
75 };
78class ConfigStore {
79 constructor(rootDir) {
80 this.rootDir = rootDir;
81 this.filePath = path.join(rootDir, "config", "config.json");
82 this.value = clone(DEFAULT_CONFIG);
83 }
85 load() {
86 try {
87 const raw = fs.readFileSync(this.filePath, "utf8");
88 this.value = normalizeConfig(JSON.parse(raw));
89 } catch (error) {
90 if (error?.code !== "ENOENT") {
91 const backup = `${this.filePath}.invalid-${Date.now()}`;
92 try { fs.renameSync(this.filePath, backup); } catch {}
93 }
94 this.value = clone(DEFAULT_CONFIG);
95 this.save();
96 }
97 return this.get();
98 }
100 get() {
101 return clone(this.value);
102 }
104 update(patch = {}) {
105 const updatePatch = patch.updates && typeof patch.updates === "object" ? patch.updates : {};
106 const next = {
107 ...this.value,
108 ui: { ...this.value.ui, ...(patch.ui || {}) },
109 updates: {
110 ...this.value.updates,
111 channel: updatePatch.channel ?? this.value.updates.channel,
112 autoCheck: updatePatch.autoCheck ?? this.value.updates.autoCheck
113 },
114 plugins: { ...this.value.plugins, ...(patch.plugins || {}) },
115 security: { ...this.value.security, ...(patch.security || {}) }
116 };
117 this.value = normalizeConfig(next);
118 this.save();
119 return this.get();
120 }
122 recordLaunchedRelease(release) {
123 const marker = normalizeReleaseMarker(release);
124 if (!marker) throw new Error("Invalid launched update release metadata.");
125 this.value = normalizeConfig({
126 ...this.value,
127 updates: { ...this.value.updates, lastLaunchedRelease: marker }
128 });
129 this.save();
130 return clone(marker);
131 }
133 save() {
134 atomicWriteJson(this.filePath, this.value);
135 }
138module.exports = {
139 ConfigStore,
140 CATALOG_CHANNELS,
141 DEFAULT_CONFIG,
142 SUPPORTED_LANGUAGES,
143 normalizeReleaseMarker,
144 normalizeConfig
145};

SHA-256: ef3ed13e02e6f67b19cdd3c7d417dcb17d779acad4ded7aec1b8d312349edc1e

SHA-256 archivu: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0