@xiboplayer/utils 0.7.3 → 0.7.4
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 +2 -2
- package/src/config.js +22 -19
- package/src/config.test.js +6 -4
- package/src/idb.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/utils",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Shared utilities for Xibo Player packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"./config": "./src/config.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@xiboplayer/crypto": "0.7.
|
|
15
|
+
"@xiboplayer/crypto": "0.7.4"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"vitest": "^2.1.9"
|
package/src/config.js
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { generateRsaKeyPair, isValidPemKey } from '@xiboplayer/crypto';
|
|
16
16
|
import { openIDB } from './idb.js';
|
|
17
|
+
import { createLogger } from './logger.js';
|
|
18
|
+
|
|
19
|
+
const log = createLogger('Config');
|
|
17
20
|
|
|
18
21
|
const GLOBAL_KEY = 'xibo_global'; // Device identity (all CMSes)
|
|
19
22
|
const CMS_PREFIX = 'xibo_cms:'; // Per-CMS config prefix
|
|
@@ -135,7 +138,7 @@ export class Config {
|
|
|
135
138
|
try {
|
|
136
139
|
global = JSON.parse(localStorage.getItem(GLOBAL_KEY) || '{}');
|
|
137
140
|
} catch (e) {
|
|
138
|
-
|
|
141
|
+
log.error('Failed to parse xibo_global:', e);
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
// Determine active CMS
|
|
@@ -148,7 +151,7 @@ export class Config {
|
|
|
148
151
|
const cmsJson = localStorage.getItem(CMS_PREFIX + activeCmsId);
|
|
149
152
|
if (cmsJson) cmsConfig = JSON.parse(cmsJson);
|
|
150
153
|
} catch (e) {
|
|
151
|
-
|
|
154
|
+
log.error('Failed to parse CMS config:', e);
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -175,16 +178,16 @@ export class Config {
|
|
|
175
178
|
let changed = false;
|
|
176
179
|
|
|
177
180
|
if (!config.hardwareKey || config.hardwareKey.length < 10) {
|
|
178
|
-
|
|
181
|
+
log.warn('Missing/invalid hardwareKey — generating');
|
|
179
182
|
config.hardwareKey = this.generateStableHardwareKey();
|
|
180
183
|
this._backupHardwareKey(config.hardwareKey);
|
|
181
184
|
changed = true;
|
|
182
185
|
} else {
|
|
183
|
-
|
|
186
|
+
log.info('✓ Loaded existing hardwareKey:', config.hardwareKey);
|
|
184
187
|
}
|
|
185
188
|
|
|
186
189
|
if (!config.xmrChannel) {
|
|
187
|
-
|
|
190
|
+
log.warn('Missing xmrChannel — generating');
|
|
188
191
|
config.xmrChannel = this.generateXmrChannel();
|
|
189
192
|
changed = true;
|
|
190
193
|
}
|
|
@@ -265,12 +268,12 @@ export class Config {
|
|
|
265
268
|
try {
|
|
266
269
|
cmsConfig = JSON.parse(existingJson);
|
|
267
270
|
isNew = false;
|
|
268
|
-
|
|
271
|
+
log.info(`Switching to existing CMS profile: ${newCmsId}`);
|
|
269
272
|
} catch (e) {
|
|
270
|
-
|
|
273
|
+
log.error('Failed to parse target CMS config:', e);
|
|
271
274
|
}
|
|
272
275
|
} else {
|
|
273
|
-
|
|
276
|
+
log.info(`Creating new CMS profile: ${newCmsId}`);
|
|
274
277
|
cmsConfig = {
|
|
275
278
|
cmsUrl,
|
|
276
279
|
cmsKey: '',
|
|
@@ -361,7 +364,7 @@ export class Config {
|
|
|
361
364
|
store.put(v, k);
|
|
362
365
|
}
|
|
363
366
|
tx.oncomplete = () => {
|
|
364
|
-
|
|
367
|
+
log.info('Keys backed up to IndexedDB:', Object.keys(keys).join(', '));
|
|
365
368
|
db.close();
|
|
366
369
|
};
|
|
367
370
|
} catch (e) {
|
|
@@ -395,8 +398,8 @@ export class Config {
|
|
|
395
398
|
db.close();
|
|
396
399
|
|
|
397
400
|
if (backedUpKey && backedUpKey !== this.data.hardwareKey) {
|
|
398
|
-
|
|
399
|
-
|
|
401
|
+
log.info('Restoring hardware key from IndexedDB backup:', backedUpKey);
|
|
402
|
+
log.info('(was:', this.data.hardwareKey, ')');
|
|
400
403
|
this.data.hardwareKey = backedUpKey;
|
|
401
404
|
this.save();
|
|
402
405
|
} else if (!backedUpKey && this.data.hardwareKey) {
|
|
@@ -417,7 +420,7 @@ export class Config {
|
|
|
417
420
|
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
418
421
|
const uuid = crypto.randomUUID().replace(/-/g, ''); // Remove dashes
|
|
419
422
|
const hardwareKey = 'pwa-' + uuid.substring(0, 28);
|
|
420
|
-
|
|
423
|
+
log.info('Generated new UUID-based hardware key:', hardwareKey);
|
|
421
424
|
return hardwareKey;
|
|
422
425
|
}
|
|
423
426
|
|
|
@@ -427,7 +430,7 @@ export class Config {
|
|
|
427
430
|
).join('');
|
|
428
431
|
|
|
429
432
|
const hardwareKey = 'pwa-' + randomHex;
|
|
430
|
-
|
|
433
|
+
log.info('Generated new random hardware key:', hardwareKey);
|
|
431
434
|
return hardwareKey;
|
|
432
435
|
}
|
|
433
436
|
|
|
@@ -450,7 +453,7 @@ export class Config {
|
|
|
450
453
|
return;
|
|
451
454
|
}
|
|
452
455
|
|
|
453
|
-
|
|
456
|
+
log.info('Generating RSA key pair for XMR registration...');
|
|
454
457
|
const { publicKeyPem, privateKeyPem } = await generateRsaKeyPair();
|
|
455
458
|
|
|
456
459
|
this.data.xmrPubKey = publicKeyPem;
|
|
@@ -462,7 +465,7 @@ export class Config {
|
|
|
462
465
|
this._backupKeys({ xmrPubKey: publicKeyPem, xmrPrivKey: privateKeyPem });
|
|
463
466
|
}
|
|
464
467
|
|
|
465
|
-
|
|
468
|
+
log.info('RSA key pair generated and saved');
|
|
466
469
|
}
|
|
467
470
|
|
|
468
471
|
get cmsUrl() { return this.data.cmsUrl; }
|
|
@@ -477,7 +480,7 @@ export class Config {
|
|
|
477
480
|
get hardwareKey() {
|
|
478
481
|
// CRITICAL: Ensure hardware key never becomes undefined
|
|
479
482
|
if (!this.data.hardwareKey) {
|
|
480
|
-
|
|
483
|
+
log.error('CRITICAL: hardwareKey missing! Generating emergency key.');
|
|
481
484
|
this.data.hardwareKey = this.generateStableHardwareKey();
|
|
482
485
|
this.save();
|
|
483
486
|
}
|
|
@@ -485,7 +488,7 @@ export class Config {
|
|
|
485
488
|
}
|
|
486
489
|
get xmrChannel() {
|
|
487
490
|
if (!this.data.xmrChannel) {
|
|
488
|
-
|
|
491
|
+
log.warn('xmrChannel missing at access time — generating');
|
|
489
492
|
this.data.xmrChannel = this.generateXmrChannel();
|
|
490
493
|
this.save();
|
|
491
494
|
}
|
|
@@ -538,8 +541,8 @@ export function warnPlatformMismatch(configObj, platform) {
|
|
|
538
541
|
const p = platform.toLowerCase();
|
|
539
542
|
for (const [key, platforms] of Object.entries(PLATFORM_KEYS)) {
|
|
540
543
|
if (key in configObj && !platforms.includes(p)) {
|
|
541
|
-
|
|
542
|
-
`
|
|
544
|
+
log.warn(
|
|
545
|
+
`Key "${key}" is only supported on ${platforms.join('/')}, ` +
|
|
543
546
|
`but current platform is ${p} — this key will be ignored`
|
|
544
547
|
);
|
|
545
548
|
}
|
package/src/config.test.js
CHANGED
|
@@ -499,16 +499,18 @@ describe('Config', () => {
|
|
|
499
499
|
warnPlatformMismatch({ browser: 'chrome', cmsUrl: 'https://test.com' }, 'electron');
|
|
500
500
|
|
|
501
501
|
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
502
|
-
|
|
503
|
-
expect(warnSpy.mock.calls[0][
|
|
502
|
+
// Logger outputs: console.warn(timestamp_prefix, message) — message is in args[1]
|
|
503
|
+
expect(warnSpy.mock.calls[0][1]).toContain('browser');
|
|
504
|
+
expect(warnSpy.mock.calls[0][1]).toContain('chromium');
|
|
504
505
|
});
|
|
505
506
|
|
|
506
507
|
it('should warn when Electron-only key is used in Chromium', () => {
|
|
507
508
|
warnPlatformMismatch({ autoLaunch: true }, 'chromium');
|
|
508
509
|
|
|
509
510
|
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
510
|
-
|
|
511
|
-
expect(warnSpy.mock.calls[0][
|
|
511
|
+
// Logger outputs: console.warn(timestamp_prefix, message) — message is in args[1]
|
|
512
|
+
expect(warnSpy.mock.calls[0][1]).toContain('autoLaunch');
|
|
513
|
+
expect(warnSpy.mock.calls[0][1]).toContain('electron');
|
|
512
514
|
});
|
|
513
515
|
|
|
514
516
|
it('should not warn for shared keys', () => {
|
package/src/idb.js
CHANGED