livedesk 0.1.488 → 0.1.490

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.
@@ -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
- this.record = {
33
- revision: Math.max(0, Number(raw?.revision) || 0),
34
- updatedAt: String(raw?.updatedAt || ''),
35
- settings: normalizeLiveDeskSettings(raw?.settings || raw)
36
- };
37
- } catch {
38
- this.record = { revision: 0, updatedAt: '', settings: normalizeLiveDeskSettings(DEFAULT_LIVEDESK_SETTINGS) };
39
- }
40
- return structuredClone(this.record);
41
- }
42
-
43
- getCached() {
44
- if (!this.record) return DEFAULT_LIVEDESK_SETTINGS;
45
- return { ...this.record.settings, revision: this.record.revision };
46
- }
47
-
48
- async get() {
49
- return publicSettings((await this.getRecord()).settings);
50
- }
51
-
52
- async update(patch = {}, expectedRevision = undefined) {
53
- const current = await this.getRecord();
54
- if (expectedRevision !== undefined && Number(expectedRevision) !== current.revision) {
55
- throw new SettingsConflictError({ revision: current.revision, updatedAt: current.updatedAt, settings: publicSettings(current.settings) });
56
- }
57
- const source = patch && typeof patch === 'object' && !Array.isArray(patch) ? patch : {};
58
- const merged = {};
59
- for (const section of Object.keys(current.settings)) {
60
- if (section === 'settingsSchemaVersion') continue;
61
- merged[section] = { ...current.settings[section], ...(source[section] && typeof source[section] === 'object' ? source[section] : {}) };
62
- }
63
- const next = {
64
- revision: current.revision + 1,
65
- updatedAt: new Date().toISOString(),
66
- settings: normalizeLiveDeskSettings(merged)
67
- };
68
- await writeJsonAtomic(this.filePath, next);
69
- this.record = next;
70
- return structuredClone(next);
71
- }
72
-
73
- async reset() {
74
- const current = await this.getRecord();
75
- return this.update(normalizeLiveDeskSettings(DEFAULT_LIVEDESK_SETTINGS), current.revision);
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
+ }
@@ -180,7 +180,7 @@ export function createSecureDirectAcceptor({
180
180
  remoteAddress: ip
181
181
  });
182
182
  try { rawSocket.write(`${JSON.stringify({ type: 'secure.error', protocol: SECURE_SESSION_PROTOCOL, error: code })}\n`); } catch {}
183
- logWarn('security', `Secure Direct handshake rejected reason=${code}`);
183
+ logWarn('security', `Secure Direct handshake rejected reason=${code} remote=${ip || 'unknown'}`);
184
184
  rawSocket.destroy();
185
185
  }
186
186
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.488",
4
- "livedeskClientVersion": "0.1.232",
3
+ "version": "0.1.490",
4
+ "livedeskClientVersion": "0.1.233",
5
5
  "buildFlavor": "production",
6
6
  "description": "LiveDesk Hub and client launcher",
7
7
  "type": "module",