CH-J Server Managerserverihaldus üle SSH
Menüü
Avaldatud allikas

CH-J Server Manager

Vaata katalooge ja faile konkreetse rakenduse vabastamiseks.

Laadi alla allikas ZIP
CH-J / Server Manager 377 failid
core-20261019T111616Z-160a00bfcddc Lisatud: · Ehitatud:
configStore.js Lisatud: Kuupäeva ei registreerita 4,9 KB · 146 read
Kood Faili allalaadimine
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: "dark",
15 navigationOrder: []
16 },
17 updates: {
18 channel: "alpha",
19 autoCheck: true,
20 lastLaunchedRelease: null,
21 baseUrls: [
22 "https://sm.ch-j.de/"
23 ]
24 },
25 plugins: {
26 channel: "alpha",
27 display: {}
28 },
29 security: { autoLockMinutes: 0, lockOnBlur: false, requireSystemAuthentication: false }
30});
32function clone(value) {
33 return JSON.parse(JSON.stringify(value));
36function normalizeReleaseMarker(value) {
37 if (!value || typeof value !== "object" || Array.isArray(value)) return null;
38 const id = String(value.id || "").trim();
39 const version = String(value.version || "").trim();
40 const publishedAt = String(value.publishedAt || "").trim();
41 const sha512 = String(value.sha512 || "").trim().toLowerCase();
42 if (!id || !version || Number.isNaN(Date.parse(publishedAt)) || !/^[a-f0-9]{128}$/.test(sha512)) return null;
43 return { id, version, publishedAt, sha512 };
46function normalizeConfig(input = {}) {
47 const source = input && typeof input === "object" && !Array.isArray(input) ? input : {};
48 const channel = CATALOG_CHANNELS.includes(source.updates?.channel)
49 ? source.updates.channel
50 : DEFAULT_CONFIG.updates.channel;
51 const pluginChannel = CATALOG_CHANNELS.includes(source.plugins?.channel)
52 ? source.plugins.channel
53 : DEFAULT_CONFIG.plugins.channel;
54 const theme = ["system", "dark", "light"].includes(source.ui?.theme)
55 ? source.ui.theme
56 : DEFAULT_CONFIG.ui.theme;
57 const language = SUPPORTED_LANGUAGES.includes(String(source.ui?.language || ""))
58 ? String(source.ui.language)
59 : DEFAULT_CONFIG.ui.language;
61 return {
62 schemaVersion: 1,
63 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) : [] },
64 updates: {
65 channel,
66 autoCheck: source.updates?.autoCheck !== false,
67 lastLaunchedRelease: normalizeReleaseMarker(source.updates?.lastLaunchedRelease),
68 baseUrls: clone(DEFAULT_CONFIG.updates.baseUrls)
69 },
70 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))) },
71 security: {
72 autoLockMinutes: [0, 1, 5, 10, 15, 30, 60].includes(source.security?.autoLockMinutes) ? source.security.autoLockMinutes : 0,
73 lockOnBlur: source.security?.lockOnBlur === true,
74 requireSystemAuthentication: source.security?.requireSystemAuthentication === true
75 }
76 };
79class ConfigStore {
80 constructor(rootDir) {
81 this.rootDir = rootDir;
82 this.filePath = path.join(rootDir, "config", "config.json");
83 this.value = clone(DEFAULT_CONFIG);
84 }
86 load() {
87 try {
88 const raw = fs.readFileSync(this.filePath, "utf8");
89 this.value = normalizeConfig(JSON.parse(raw));
90 } catch (error) {
91 if (error?.code !== "ENOENT") {
92 const backup = `${this.filePath}.invalid-${Date.now()}`;
93 try { fs.renameSync(this.filePath, backup); } catch {}
94 }
95 this.value = clone(DEFAULT_CONFIG);
96 this.save();
97 }
98 return this.get();
99 }
101 get() {
102 return clone(this.value);
103 }
105 update(patch = {}) {
106 const updatePatch = patch.updates && typeof patch.updates === "object" ? patch.updates : {};
107 const next = {
108 ...this.value,
109 ui: { ...this.value.ui, ...(patch.ui || {}) },
110 updates: {
111 ...this.value.updates,
112 channel: updatePatch.channel ?? this.value.updates.channel,
113 autoCheck: updatePatch.autoCheck ?? this.value.updates.autoCheck
114 },
115 plugins: { ...this.value.plugins, ...(patch.plugins || {}) },
116 security: { ...this.value.security, ...(patch.security || {}) }
117 };
118 this.value = normalizeConfig(next);
119 this.save();
120 return this.get();
121 }
123 recordLaunchedRelease(release) {
124 const marker = normalizeReleaseMarker(release);
125 if (!marker) throw new Error("Invalid launched update release metadata.");
126 this.value = normalizeConfig({
127 ...this.value,
128 updates: { ...this.value.updates, lastLaunchedRelease: marker }
129 });
130 this.save();
131 return clone(marker);
132 }
134 save() {
135 atomicWriteJson(this.filePath, this.value);
136 }
139module.exports = {
140 ConfigStore,
141 CATALOG_CHANNELS,
142 DEFAULT_CONFIG,
143 SUPPORTED_LANGUAGES,
144 normalizeReleaseMarker,
145 normalizeConfig
146};
SHA-256c5b49d76b57f817af19928a45c27facb98282e5e606429b1a616ec0b5c0f3090

CH-J Proprietary Software License 1.14 · Allikas on esitatud CH-J Proprietary Software License 1.14 all. Selle kättesaadavus ei muuda litsentsitingimusi ega anna täiendavaid õigusi.

Arhiiv SHA-256a9605c0870a223b6a8542bb0705edd51d8b04f3069582653417a8397fabcc09a
Failid