@livedesk/hub 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +32 -32
- package/src/remote-hub.js +782 -728
- package/src/server.js +1058 -980
- package/src/settings/settings-schema.js +251 -239
- package/src/settings/settings-store.js +82 -77
- package/src/transport/secure-direct-acceptor.js +440 -433
|
@@ -1,77 +1,82 @@
|
|
|
1
|
-
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { DEFAULT_LIVEDESK_SETTINGS, defaultSettingsPath, normalizeLiveDeskSettings, publicSettings } from './settings-schema.js';
|
|
4
|
-
|
|
5
|
-
export class SettingsConflictError extends Error {
|
|
6
|
-
constructor(settings) {
|
|
7
|
-
super('Settings changed in another session. Reload before saving again.');
|
|
8
|
-
this.name = 'SettingsConflictError';
|
|
9
|
-
this.code = 'settings-revision-conflict';
|
|
10
|
-
this.status = 409;
|
|
11
|
-
this.settings = settings;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function writeJsonAtomic(filePath, value) {
|
|
16
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
17
|
-
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
18
|
-
await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
19
|
-
await rename(temporaryPath, filePath);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class LiveDeskSettingsStore {
|
|
23
|
-
constructor({ dataDir } = {}) {
|
|
24
|
-
this.filePath = defaultSettingsPath(dataDir);
|
|
25
|
-
this.record = null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async getRecord() {
|
|
29
|
-
if (this.record) return structuredClone(this.record);
|
|
30
|
-
try {
|
|
31
|
-
const raw = JSON.parse(await readFile(this.filePath, 'utf8'));
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
settings:
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_LIVEDESK_SETTINGS, SETTINGS_SCHEMA_VERSION, defaultSettingsPath, migrateLiveDeskSettings, normalizeLiveDeskSettings, publicSettings } from './settings-schema.js';
|
|
4
|
+
|
|
5
|
+
export class SettingsConflictError extends Error {
|
|
6
|
+
constructor(settings) {
|
|
7
|
+
super('Settings changed in another session. Reload before saving again.');
|
|
8
|
+
this.name = 'SettingsConflictError';
|
|
9
|
+
this.code = 'settings-revision-conflict';
|
|
10
|
+
this.status = 409;
|
|
11
|
+
this.settings = settings;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function writeJsonAtomic(filePath, value) {
|
|
16
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
17
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
18
|
+
await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
19
|
+
await rename(temporaryPath, filePath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class LiveDeskSettingsStore {
|
|
23
|
+
constructor({ dataDir } = {}) {
|
|
24
|
+
this.filePath = defaultSettingsPath(dataDir);
|
|
25
|
+
this.record = null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getRecord() {
|
|
29
|
+
if (this.record) return structuredClone(this.record);
|
|
30
|
+
try {
|
|
31
|
+
const raw = JSON.parse(await readFile(this.filePath, 'utf8'));
|
|
32
|
+
const sourceSettings = raw?.settings || raw;
|
|
33
|
+
const sourceSchemaVersion = Math.max(0, Number(sourceSettings?.settingsSchemaVersion) || 0);
|
|
34
|
+
this.record = {
|
|
35
|
+
revision: Math.max(0, Number(raw?.revision) || 0),
|
|
36
|
+
updatedAt: String(raw?.updatedAt || ''),
|
|
37
|
+
settings: migrateLiveDeskSettings(sourceSettings)
|
|
38
|
+
};
|
|
39
|
+
if (sourceSchemaVersion < SETTINGS_SCHEMA_VERSION) {
|
|
40
|
+
await writeJsonAtomic(this.filePath, this.record);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
this.record = { revision: 0, updatedAt: '', settings: normalizeLiveDeskSettings(DEFAULT_LIVEDESK_SETTINGS) };
|
|
44
|
+
}
|
|
45
|
+
return structuredClone(this.record);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getCached() {
|
|
49
|
+
if (!this.record) return DEFAULT_LIVEDESK_SETTINGS;
|
|
50
|
+
return { ...this.record.settings, revision: this.record.revision };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async get() {
|
|
54
|
+
return publicSettings((await this.getRecord()).settings);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async update(patch = {}, expectedRevision = undefined) {
|
|
58
|
+
const current = await this.getRecord();
|
|
59
|
+
if (expectedRevision !== undefined && Number(expectedRevision) !== current.revision) {
|
|
60
|
+
throw new SettingsConflictError({ revision: current.revision, updatedAt: current.updatedAt, settings: publicSettings(current.settings) });
|
|
61
|
+
}
|
|
62
|
+
const source = patch && typeof patch === 'object' && !Array.isArray(patch) ? patch : {};
|
|
63
|
+
const merged = {};
|
|
64
|
+
for (const section of Object.keys(current.settings)) {
|
|
65
|
+
if (section === 'settingsSchemaVersion') continue;
|
|
66
|
+
merged[section] = { ...current.settings[section], ...(source[section] && typeof source[section] === 'object' ? source[section] : {}) };
|
|
67
|
+
}
|
|
68
|
+
const next = {
|
|
69
|
+
revision: current.revision + 1,
|
|
70
|
+
updatedAt: new Date().toISOString(),
|
|
71
|
+
settings: normalizeLiveDeskSettings(merged)
|
|
72
|
+
};
|
|
73
|
+
await writeJsonAtomic(this.filePath, next);
|
|
74
|
+
this.record = next;
|
|
75
|
+
return structuredClone(next);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async reset() {
|
|
79
|
+
const current = await this.getRecord();
|
|
80
|
+
return this.update(normalizeLiveDeskSettings(DEFAULT_LIVEDESK_SETTINGS), current.revision);
|
|
81
|
+
}
|
|
82
|
+
}
|