CH-J Server Managermanagementul serverului peste SSH
Meniu
Sursă publicată

CH-J Server Manager

Navighează directoarele și fișierele pentru o anumită versiune a aplicației.

Descarcă sursa ZIP
CH-J / Server Manager 339 fișiere
core-20261014T184258Z-3fbed0b93fdf Adăugat: Data neînregistrată · Construit:
Fișiere
configStore.js Adăugat: Data neînregistrată 5,0 KB · 147 linii
Descarcă fișierul
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 navigationOrder: []
16 },
17 updates: {
18 channel: "alpha",
19 autoCheck: true,
20 lastLaunchedRelease: null,
21 baseUrls: [
22 "https://www.sm.ch-j.de/",
23 "https://sm.ch-j.de/"
24 ]
25 },
26 plugins: {
27 channel: "alpha",
28 display: {}
29 },
30 security: { autoLockMinutes: 0, lockOnBlur: false, requireSystemAuthentication: false }
31});
33function clone(value) {
34 return JSON.parse(JSON.stringify(value));
37function normalizeReleaseMarker(value) {
38 if (!value || typeof value !== "object" || Array.isArray(value)) return null;
39 const id = String(value.id || "").trim();
40 const version = String(value.version || "").trim();
41 const publishedAt = String(value.publishedAt || "").trim();
42 const sha512 = String(value.sha512 || "").trim().toLowerCase();
43 if (!id || !version || Number.isNaN(Date.parse(publishedAt)) || !/^[a-f0-9]{128}$/.test(sha512)) return null;
44 return { id, version, publishedAt, sha512 };
47function normalizeConfig(input = {}) {
48 const source = input && typeof input === "object" && !Array.isArray(input) ? input : {};
49 const channel = CATALOG_CHANNELS.includes(source.updates?.channel)
50 ? source.updates.channel
51 : DEFAULT_CONFIG.updates.channel;
52 const pluginChannel = CATALOG_CHANNELS.includes(source.plugins?.channel)
53 ? source.plugins.channel
54 : DEFAULT_CONFIG.plugins.channel;
55 const theme = ["system", "dark", "light"].includes(source.ui?.theme)
56 ? source.ui.theme
57 : DEFAULT_CONFIG.ui.theme;
58 const language = SUPPORTED_LANGUAGES.includes(String(source.ui?.language || ""))
59 ? String(source.ui.language)
60 : DEFAULT_CONFIG.ui.language;
62 return {
63 schemaVersion: 1,
64 ui: { language, theme, navigationOrder: Array.isArray(source.ui?.navigationOrder) ? [...new Set(source.ui.navigationOrder.filter((id) => typeof id === "string" && /^(overview|terminal|profiles|updates|plugins|diagnostics|settings|plugin:[a-z0-9][a-z0-9.-]{2,63})$/.test(id)))].slice(0, 200) : [] },
65 updates: {
66 channel,
67 autoCheck: source.updates?.autoCheck !== false,
68 lastLaunchedRelease: normalizeReleaseMarker(source.updates?.lastLaunchedRelease),
69 baseUrls: clone(DEFAULT_CONFIG.updates.baseUrls)
70 },
71 plugins: { channel: pluginChannel, display: Object.fromEntries(Object.entries(source.plugins?.display && typeof source.plugins.display === "object" && !Array.isArray(source.plugins.display) ? source.plugins.display : {}).filter(([id, mode]) => /^[a-z0-9][a-z0-9.-]{2,63}$/.test(id) && ["hidden", "visible", "navigation"].includes(mode))) },
72 security: {
73 autoLockMinutes: [0, 1, 5, 10, 15, 30, 60].includes(source.security?.autoLockMinutes) ? source.security.autoLockMinutes : 0,
74 lockOnBlur: source.security?.lockOnBlur === true,
75 requireSystemAuthentication: source.security?.requireSystemAuthentication === true
76 }
77 };
80class ConfigStore {
81 constructor(rootDir) {
82 this.rootDir = rootDir;
83 this.filePath = path.join(rootDir, "config", "config.json");
84 this.value = clone(DEFAULT_CONFIG);
85 }
87 load() {
88 try {
89 const raw = fs.readFileSync(this.filePath, "utf8");
90 this.value = normalizeConfig(JSON.parse(raw));
91 } catch (error) {
92 if (error?.code !== "ENOENT") {
93 const backup = `${this.filePath}.invalid-${Date.now()}`;
94 try { fs.renameSync(this.filePath, backup); } catch {}
95 }
96 this.value = clone(DEFAULT_CONFIG);
97 this.save();
98 }
99 return this.get();
100 }
102 get() {
103 return clone(this.value);
104 }
106 update(patch = {}) {
107 const updatePatch = patch.updates && typeof patch.updates === "object" ? patch.updates : {};
108 const next = {
109 ...this.value,
110 ui: { ...this.value.ui, ...(patch.ui || {}) },
111 updates: {
112 ...this.value.updates,
113 channel: updatePatch.channel ?? this.value.updates.channel,
114 autoCheck: updatePatch.autoCheck ?? this.value.updates.autoCheck
115 },
116 plugins: { ...this.value.plugins, ...(patch.plugins || {}) },
117 security: { ...this.value.security, ...(patch.security || {}) }
118 };
119 this.value = normalizeConfig(next);
120 this.save();
121 return this.get();
122 }
124 recordLaunchedRelease(release) {
125 const marker = normalizeReleaseMarker(release);
126 if (!marker) throw new Error("Invalid launched update release metadata.");
127 this.value = normalizeConfig({
128 ...this.value,
129 updates: { ...this.value.updates, lastLaunchedRelease: marker }
130 });
131 this.save();
132 return clone(marker);
133 }
135 save() {
136 atomicWriteJson(this.filePath, this.value);
137 }
140module.exports = {
141 ConfigStore,
142 CATALOG_CHANNELS,
143 DEFAULT_CONFIG,
144 SUPPORTED_LANGUAGES,
145 normalizeReleaseMarker,
146 normalizeConfig
147};
SHA-256744d184ccea7c6979d4a111b89a3b8e6ec7f8ee3a602242b7e1e669335ca187f

CH-J Proprietary Software License 1.14 · Sursa este furnizată în cadrul CH-J Proprietary Software License 1.14. Disponibilitatea sa nu modifică termenii licenţei sau nu acordă drepturi suplimentare.

Archive SHA-2563df08cf5e899262a6ce92d7ec2caf5c42b683f1f2b57055662b845ff7dde67a8