iobroker.eos-admin 7.10.0 → 7.10.2

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.
Files changed (65) hide show
  1. package/INSTALL_TEST_V7.10.2.md +22 -0
  2. package/MERGE_UPDATE_README_V7.10.2.md +11 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +19 -21
  4. package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +1410 -0
  5. package/PUBLISH_STABLE_V7.10.2.md +21 -0
  6. package/README.md +16 -6
  7. package/README_STABILITY_V7.10.2.md +20 -0
  8. package/RELEASE_ACCEPTANCE_V7.10.2.md +13 -0
  9. package/RELEASE_NOTES_V7.10.2.md +11 -0
  10. package/adminWww/assets/Intro-DkwRiz1n-v84.js +1 -1
  11. package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
  12. package/adminWww/css/eos-ems-overview.css +5 -3
  13. package/adminWww/css/nexowatt-native-shell.css +85 -413
  14. package/adminWww/index.html +20 -21
  15. package/adminWww/js/eos-account-management.js +28 -19
  16. package/adminWww/js/eos-assistant.js +1 -1
  17. package/adminWww/js/eos-ems-overview.js +1 -1
  18. package/adminWww/js/eos-role-bootstrap.js +17 -80
  19. package/adminWww/js/eos-role-ui.js +28 -4
  20. package/adminWww/js/nexowatt-native-shell.js +1 -1
  21. package/adminWww/static/js/nexowatt-stable-login.js +43 -70
  22. package/adminWww/static/js/nexowatt-stable-v7101.js +27 -0
  23. package/build/lib/eosAutoUpdate.js +418 -20
  24. package/build/lib/eosAutoUpdate.js.map +1 -0
  25. package/build/lib/eosRequestSecurity.js +4 -1
  26. package/build/lib/eosRequestSecurity.js.map +1 -0
  27. package/build/lib/web.js +161 -254
  28. package/build/lib/web.js.map +1 -1
  29. package/build/main.js +39 -67
  30. package/io-package.json +72 -7
  31. package/package.json +24 -25
  32. package/tools/nexowatt-account-management-selftest.cjs +15 -19
  33. package/tools/nexowatt-account-password-flow-selftest.cjs +15 -0
  34. package/tools/nexowatt-apply-ems-overview.cjs +4 -2
  35. package/tools/nexowatt-auto-update-selftest.cjs +144 -32
  36. package/tools/nexowatt-backend-runtime-selftest.cjs +147 -0
  37. package/tools/nexowatt-clean-legacy-runtime.cjs +1 -1
  38. package/tools/nexowatt-ems-overview-selftest.cjs +1 -1
  39. package/tools/nexowatt-ensure-release-defaults.cjs +1 -1
  40. package/tools/nexowatt-login-layout-selftest.cjs +9 -13
  41. package/tools/nexowatt-merge-update-selftest.cjs +7 -1
  42. package/tools/nexowatt-modern-ui-selftest.cjs +10 -46
  43. package/tools/nexowatt-password-write-selftest.cjs +46 -16
  44. package/tools/nexowatt-prebuilt-release-selftest.cjs +292 -0
  45. package/tools/nexowatt-release-defaults-selftest.cjs +1 -1
  46. package/tools/nexowatt-runtime-cleanup-selftest.cjs +6 -8
  47. package/tools/nexowatt-stable-v7102-selftest.cjs +51 -0
  48. package/tools/nexowatt-sync-release-version.cjs +43 -5
  49. package/tools/nexowatt-ui-overview-runtime-selftest.cjs +2 -3
  50. package/tools/nexowatt-validate-package.cjs +54 -29
  51. package/tools/nexowatt-version-sync-selftest.cjs +18 -1
  52. package/INSTALL_TEST_V7.10.0.md +0 -11
  53. package/MERGE_UPDATE_README_V7.10.0.md +0 -5
  54. package/PUBLISH_STABLE_V7.10.0.md +0 -15
  55. package/README_STABILITY_V7.10.0.md +0 -3
  56. package/RELEASE_ACCEPTANCE_V7.10.0.md +0 -11
  57. package/RELEASE_NOTES_V7.10.0.md +0 -17
  58. package/adminWww/js/eos-release-watch.js +0 -77
  59. package/adminWww/static/js/nexowatt-stable-v7100.js +0 -111
  60. package/build/lib/eosPassword.js +0 -72
  61. package/build/lib/nexowattStableAuth.js +0 -74
  62. package/src/lib/eosAutoUpdate.ts +0 -320
  63. package/tools/nexowatt-cache-update-selftest.cjs +0 -12
  64. package/tools/nexowatt-request-security-selftest.cjs +0 -11
  65. package/tools/nexowatt-stable-v7100-selftest.cjs +0 -17
@@ -1,74 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.startNexowattStableAuth = startNexowattStableAuth;
4
- const START_PASSWORD = 'nexowatt';
5
- const MARKER_KEYS = ["eosFirstLoginPending", "eosMustChangePassword", "eosPasswordSetupRequired", "firstLoginRequired", "mustChangePassword", "nexowattFirstLoginPending", "nexowattMustChangePassword", "nexowattPasswordSetupRequired", "passwordChangeRequired"];
6
- const timers = new WeakMap();
7
- function pendingByNative(native) {
8
- if (!native || typeof native !== 'object') return false;
9
- return MARKER_KEYS.some(key => native[key] === true || native[key] === 'pending' || native[key] === 'required' || native[key] === 'starter');
10
- }
11
- async function setPassword(adapter, user, password) {
12
- if (typeof adapter.setPasswordAsync === 'function') return adapter.setPasswordAsync(user, password, { user: 'system.user.admin' });
13
- return new Promise((resolve, reject) => adapter.setPassword(user, password, { user: 'system.user.admin' }, err => err ? reject(err) : resolve()));
14
- }
15
- async function ensureGroupMembership(adapter, userId, groupId) {
16
- const group = await adapter.getForeignObjectAsync(groupId);
17
- if (!group || group.type !== 'group') return;
18
- group.common = group.common || { name: groupId.split('.').pop(), members: [] };
19
- group.common.members = Array.isArray(group.common.members) ? group.common.members : [];
20
- if (!group.common.members.includes(userId)) {
21
- group.common.members.push(userId);
22
- await adapter.setForeignObjectAsync(groupId, group, { user: 'system.user.admin' });
23
- }
24
- }
25
- async function createUser(adapter, name) {
26
- const id = `system.user.${name}`;
27
- await adapter.setForeignObjectAsync(id, {
28
- _id: id, type: 'user',
29
- common: { name, enabled: true, password: '' },
30
- native: { nexowattManagedAccount: true, nexowattMustChangePassword: true, nexowattPasswordState: 'starter' }
31
- }, { user: 'system.user.admin' });
32
- return adapter.getForeignObjectAsync(id);
33
- }
34
- async function prepareOne(adapter, name, group, createMissing) {
35
- const id = `system.user.${name}`;
36
- let obj = await adapter.getForeignObjectAsync(id);
37
- let created = false;
38
- if (!obj && createMissing) { obj = await createUser(adapter, name); created = true; }
39
- if (!obj || obj.type !== 'user') return;
40
- obj.native = obj.native || {};
41
- const empty = !obj.common || !obj.common.password;
42
- const pending = created || empty || pendingByNative(obj.native) || obj.native.nexowattPasswordState === 'starter';
43
- if (pending) {
44
- await setPassword(adapter, name, START_PASSWORD);
45
- obj = await adapter.getForeignObjectAsync(id) || obj;
46
- obj.native = obj.native || {};
47
- obj.native.nexowattManagedAccount = true;
48
- obj.native.nexowattStarterPasswordActive = true;
49
- obj.native.nexowattMustChangePassword = true;
50
- obj.native.nexowattPasswordState = 'starter';
51
- obj.native.nexowattStarterPasswordUpdatedAt = new Date().toISOString();
52
- for (const key of MARKER_KEYS) {
53
- if (/required|must|pending|first/i.test(key)) obj.native[key] = true;
54
- }
55
- await adapter.setForeignObjectAsync(id, obj, { user: 'system.user.admin' });
56
- }
57
- await ensureGroupMembership(adapter, id, group);
58
- }
59
- async function reconcile(adapter) {
60
- try {
61
- await prepareOne(adapter, 'installer', 'system.group.installer', true);
62
- const guest = await adapter.getForeignObjectAsync('system.user.guest');
63
- const user = await adapter.getForeignObjectAsync('system.user.user');
64
- if (guest || !user) await prepareOne(adapter, 'guest', 'system.group.endkunden', true);
65
- if (user) await prepareOne(adapter, 'user', 'system.group.endkunden', false);
66
- } catch (e) { adapter.log.warn(`[NexoWatt EOS login] Starter-account reconciliation failed: ${e && e.message || e}`); }
67
- }
68
- function startNexowattStableAuth(adapter) {
69
- if (timers.has(adapter)) return;
70
- void reconcile(adapter);
71
- const timer = setInterval(() => void reconcile(adapter), 5000);
72
- if (timer.unref) timer.unref();
73
- timers.set(adapter, timer);
74
- }
@@ -1,320 +0,0 @@
1
- /**
2
- * NexoWatt stable adapter auto-update policy manager.
3
- *
4
- * This class does not install packages itself. It configures ioBroker's native
5
- * adapterAutoUpgrade manager so only installed NexoWatt adapters present in the
6
- * detected Stable repository receive the `major` policy. All other adapters and
7
- * existing user policies are preserved.
8
- */
9
-
10
- export type NexoWattAutoUpdatePolicy = 'none' | 'patch' | 'minor' | 'major';
11
-
12
- interface AdapterObjectLike {
13
- _id?: string;
14
- common?: Record<string, any>;
15
- native?: Record<string, any>;
16
- }
17
-
18
- interface NexoWattAutoUpdateAdapter {
19
- namespace: string;
20
- host?: string;
21
- log: { debug: (message: string) => void; info: (message: string) => void; warn: (message: string) => void; error: (message: string) => void };
22
- getForeignObjectAsync: (id: string) => Promise<any>;
23
- setForeignObjectAsync: (id: string, object: any, options?: any) => Promise<any>;
24
- extendForeignObjectAsync: (id: string, object: any, options?: any) => Promise<any>;
25
- getObjectViewAsync: (design: string, search: string, params: any, options?: any) => Promise<any>;
26
- sendToHostAsync?: (host: string, command: string, message: any) => Promise<any>;
27
- }
28
-
29
- export interface NexoWattStableUpdateStatus {
30
- enabled: boolean;
31
- repository: string;
32
- managedAdapters: string[];
33
- availableStableAdapters: string[];
34
- lastSync: number;
35
- lastRepositoryRefresh: number;
36
- error: string;
37
- policy: 'major';
38
- source: 'ioBroker-native-auto-upgrade';
39
- }
40
-
41
- interface PersistedState {
42
- previousPolicies?: Record<string, NexoWattAutoUpdatePolicy | null>;
43
- previousRepositoryEnabled?: boolean | null;
44
- previousActiveRepo?: string[];
45
- selectedRepository?: string;
46
- ownsActiveRepoOrder?: boolean;
47
- managedAdapters?: string[];
48
- availableStableAdapters?: string[];
49
- lastSync?: number;
50
- lastRepositoryRefresh?: number;
51
- error?: string;
52
- }
53
-
54
- const ADMIN_USER = 'system.user.admin';
55
- const POLICY: NexoWattAutoUpdatePolicy = 'major';
56
- const RECONCILE_INTERVAL_MS = 6 * 60 * 60 * 1000;
57
- const REPOSITORY_REFRESH_DELAY_MS = 2500;
58
-
59
- const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value));
60
- const normalizedName = (value: unknown): string => String(value || '').trim().replace(/^system\.adapter\./, '').replace(/\.\d+$/, '');
61
- const stableVersion = (value: unknown): boolean => /^\d+\.\d+\.\d+$/.test(String(value || '').trim());
62
-
63
- export function isNexoWattRepositoryEntry(name: string, entry: any): boolean {
64
- const adapterName = normalizedName(entry?.name || name).toLowerCase();
65
- if (adapterName === 'eos-admin' || adapterName.startsWith('nexowatt-') || adapterName.startsWith('eos-')) return true;
66
- const evidence = JSON.stringify({
67
- name,
68
- adapterName,
69
- title: entry?.title,
70
- desc: entry?.desc,
71
- author: entry?.author,
72
- authors: entry?.authors,
73
- publisher: entry?.publisher,
74
- url: entry?.url,
75
- meta: entry?.meta,
76
- readme: entry?.readme,
77
- extIcon: entry?.extIcon,
78
- licenseInformation: entry?.licenseInformation,
79
- }).toLowerCase();
80
- return evidence.includes('nexowatt') || evidence.includes('github.com/nexowatt/');
81
- }
82
-
83
- export class NexoWattStableUpdateManager {
84
- private timer?: NodeJS.Timeout;
85
- private reconcilePromise?: Promise<NexoWattStableUpdateStatus>;
86
- private refreshTimer?: NodeJS.Timeout;
87
- private retryTimer?: NodeJS.Timeout;
88
-
89
- constructor(private readonly adapter: NexoWattAutoUpdateAdapter) {}
90
-
91
- private get instanceId(): string { return `system.adapter.${this.adapter.namespace}`; }
92
-
93
- async start(): Promise<NexoWattStableUpdateStatus> {
94
- const status = await this.reconcile('startup');
95
- if (!this.timer) {
96
- this.timer = setInterval(() => void this.reconcile('interval'), RECONCILE_INTERVAL_MS);
97
- this.timer.unref?.();
98
- }
99
- return status;
100
- }
101
-
102
- stop(): void {
103
- if (this.timer) clearInterval(this.timer);
104
- if (this.refreshTimer) clearTimeout(this.refreshTimer);
105
- if (this.retryTimer) clearTimeout(this.retryTimer);
106
- this.timer = undefined;
107
- this.refreshTimer = undefined;
108
- this.retryTimer = undefined;
109
- }
110
-
111
- async getStatus(refresh = false): Promise<NexoWattStableUpdateStatus> {
112
- if (refresh) return this.reconcile('status');
113
- const instance = await this.adapter.getForeignObjectAsync(this.instanceId);
114
- return this.statusFromNative(instance?.native || {});
115
- }
116
-
117
- async setEnabled(enabled: boolean): Promise<NexoWattStableUpdateStatus> {
118
- await this.adapter.extendForeignObjectAsync(this.instanceId, { native: { eosNexoWattAutoUpdate: enabled } }, { user: ADMIN_USER });
119
- return this.reconcile(enabled ? 'enabled' : 'disabled');
120
- }
121
-
122
- async reconcile(reason = 'manual'): Promise<NexoWattStableUpdateStatus> {
123
- if (this.reconcilePromise) return this.reconcilePromise;
124
- this.reconcilePromise = this.reconcileInternal(reason).finally(() => { this.reconcilePromise = undefined; });
125
- return this.reconcilePromise;
126
- }
127
-
128
- private statusFromNative(native: Record<string, any>): NexoWattStableUpdateStatus {
129
- const state = (native.eosNexoWattAutoUpdateState || {}) as PersistedState;
130
- return {
131
- enabled: native.eosNexoWattAutoUpdate !== false,
132
- repository: String(state.selectedRepository || ''),
133
- managedAdapters: Array.isArray(state.managedAdapters) ? state.managedAdapters.map(String).sort() : [],
134
- availableStableAdapters: Array.isArray(state.availableStableAdapters) ? state.availableStableAdapters.map(String).sort() : [],
135
- lastSync: Number(state.lastSync || 0),
136
- lastRepositoryRefresh: Number(state.lastRepositoryRefresh || 0),
137
- error: String(state.error || ''),
138
- policy: POLICY,
139
- source: 'ioBroker-native-auto-upgrade',
140
- };
141
- }
142
-
143
- private async reconcileInternal(reason: string): Promise<NexoWattStableUpdateStatus> {
144
- const instance = await this.adapter.getForeignObjectAsync(this.instanceId);
145
- const native = instance?.native || {};
146
- const enabled = native.eosNexoWattAutoUpdate !== false;
147
- const state = clone((native.eosNexoWattAutoUpdateState || {}) as PersistedState);
148
- try {
149
- if (!enabled) return await this.disable(state, reason);
150
- return await this.enable(state, reason);
151
- } catch (error) {
152
- const message = error instanceof Error ? error.message : String(error || 'unknownError');
153
- state.error = message;
154
- state.lastSync = Date.now();
155
- await this.persist(enabled, state);
156
- if (enabled) this.scheduleRetry();
157
- this.adapter.log.warn(`[NexoWatt stable updates] ${message}`);
158
- return this.statusFromNative({ eosNexoWattAutoUpdate: enabled, eosNexoWattAutoUpdateState: state });
159
- }
160
- }
161
-
162
- private async readInstalledAdapters(): Promise<Map<string, AdapterObjectLike>> {
163
- const view = await this.adapter.getObjectViewAsync('system', 'adapter', {
164
- startkey: 'system.adapter.',
165
- endkey: 'system.adapter.\u9999',
166
- }, { user: ADMIN_USER });
167
- const result = new Map<string, AdapterObjectLike>();
168
- for (const row of view?.rows || []) {
169
- const object = row?.value || row?.doc;
170
- const name = normalizedName(object?.common?.name || row?.id || object?._id);
171
- if (name && object?.common?.version) result.set(name, object);
172
- }
173
- return result;
174
- }
175
-
176
- private async readRepositorySelection(systemConfig: any): Promise<{ name: string; entries: Record<string, any>; stableNames: string[] }> {
177
- const repositories = await this.adapter.getForeignObjectAsync('system.repositories');
178
- const rows = repositories?.native?.repositories || {};
179
- const active = Array.isArray(systemConfig?.common?.activeRepo) ? systemConfig.common.activeRepo.map(String) : [];
180
- const candidates = Object.entries(rows).map(([name, repo]: [string, any]) => {
181
- const entries = repo?.json && typeof repo.json === 'object' ? repo.json : {};
182
- const stableNames = Object.entries(entries)
183
- .filter(([entryName, entry]) => stableVersion((entry as any)?.version) && isNexoWattRepositoryEntry(entryName, entry))
184
- .map(([entryName, entry]) => normalizedName((entry as any)?.name || entryName))
185
- .filter(Boolean);
186
- return { name, entries, stableNames: [...new Set(stableNames)].sort(), activeIndex: active.indexOf(name) };
187
- }).filter(row => row.stableNames.length > 0);
188
- if (!candidates.length) throw new Error('nexowattStableRepositoryNotFound');
189
- candidates.sort((a, b) => {
190
- const aa = a.activeIndex < 0 ? Number.MAX_SAFE_INTEGER : a.activeIndex;
191
- const bb = b.activeIndex < 0 ? Number.MAX_SAFE_INTEGER : b.activeIndex;
192
- return aa - bb || b.stableNames.length - a.stableNames.length || a.name.localeCompare(b.name);
193
- });
194
- return candidates[0];
195
- }
196
-
197
- private async enable(state: PersistedState, reason: string): Promise<NexoWattStableUpdateStatus> {
198
- const systemConfig = await this.adapter.getForeignObjectAsync('system.config');
199
- if (!systemConfig?.common) throw new Error('systemConfigUnavailable');
200
- const selected = await this.readRepositorySelection(systemConfig);
201
- const installed = await this.readInstalledAdapters();
202
- const managedNames = selected.stableNames.filter(name => installed.has(name)).sort();
203
- if (!managedNames.length) throw new Error('noInstalledNexoWattStableAdapters');
204
-
205
- state.previousPolicies ||= {};
206
- for (const name of managedNames) {
207
- if (!(name in state.previousPolicies)) {
208
- const current = installed.get(name)?.common?.automaticUpgrade;
209
- state.previousPolicies[name] = current == null ? null : current as NexoWattAutoUpdatePolicy;
210
- }
211
- await this.writeAdapterPolicy(name, POLICY);
212
- }
213
-
214
- const common = systemConfig.common;
215
- const activeRepo = Array.isArray(common.activeRepo) ? [...common.activeRepo] : [];
216
- const auto = common.adapterAutoUpgrade && typeof common.adapterAutoUpgrade === 'object'
217
- ? clone(common.adapterAutoUpgrade)
218
- : { repositories: {}, defaultPolicy: 'none' };
219
- auto.repositories = auto.repositories && typeof auto.repositories === 'object' ? { ...auto.repositories } : {};
220
- if (state.previousRepositoryEnabled === undefined) {
221
- state.previousRepositoryEnabled = Object.prototype.hasOwnProperty.call(auto.repositories, selected.name)
222
- ? Boolean(auto.repositories[selected.name])
223
- : null;
224
- }
225
- auto.repositories[selected.name] = true;
226
- if (!auto.defaultPolicy) auto.defaultPolicy = 'none';
227
- common.adapterAutoUpgrade = auto;
228
-
229
- if (activeRepo[0] !== selected.name) {
230
- if (!state.previousActiveRepo) state.previousActiveRepo = [...activeRepo];
231
- state.ownsActiveRepoOrder = true;
232
- common.activeRepo = [selected.name, ...activeRepo.filter((name: string) => name !== selected.name)];
233
- }
234
- await this.adapter.setForeignObjectAsync('system.config', systemConfig, { user: ADMIN_USER });
235
-
236
- state.selectedRepository = selected.name;
237
- state.availableStableAdapters = selected.stableNames;
238
- state.managedAdapters = managedNames;
239
- state.lastSync = Date.now();
240
- state.error = '';
241
- await this.persist(true, state);
242
- this.scheduleRepositoryRefresh(selected.name, state);
243
- this.adapter.log.info(`[NexoWatt stable updates] Enabled for ${managedNames.join(', ')} from repository "${selected.name}" (${reason})`);
244
- return this.statusFromNative({ eosNexoWattAutoUpdate: true, eosNexoWattAutoUpdateState: state });
245
- }
246
-
247
- private async disable(state: PersistedState, reason: string): Promise<NexoWattStableUpdateStatus> {
248
- for (const [name, previous] of Object.entries(state.previousPolicies || {})) {
249
- const current = await this.adapter.getForeignObjectAsync(`system.adapter.${normalizedName(name)}`);
250
- if (current?.common?.automaticUpgrade === POLICY) await this.writeAdapterPolicy(name, previous);
251
- }
252
- const systemConfig = await this.adapter.getForeignObjectAsync('system.config');
253
- if (systemConfig?.common) {
254
- const auto = systemConfig.common.adapterAutoUpgrade && typeof systemConfig.common.adapterAutoUpgrade === 'object'
255
- ? clone(systemConfig.common.adapterAutoUpgrade)
256
- : null;
257
- if (auto && state.selectedRepository) {
258
- auto.repositories = auto.repositories && typeof auto.repositories === 'object' ? { ...auto.repositories } : {};
259
- if (auto.repositories[state.selectedRepository] === true) {
260
- if (state.previousRepositoryEnabled === null || state.previousRepositoryEnabled === undefined) delete auto.repositories[state.selectedRepository];
261
- else auto.repositories[state.selectedRepository] = state.previousRepositoryEnabled;
262
- }
263
- systemConfig.common.adapterAutoUpgrade = auto;
264
- }
265
- if (state.ownsActiveRepoOrder && Array.isArray(state.previousActiveRepo)
266
- && Array.isArray(systemConfig.common.activeRepo)
267
- && systemConfig.common.activeRepo[0] === state.selectedRepository) {
268
- systemConfig.common.activeRepo = [...state.previousActiveRepo];
269
- }
270
- await this.adapter.setForeignObjectAsync('system.config', systemConfig, { user: ADMIN_USER });
271
- }
272
- state.previousPolicies = {};
273
- state.previousRepositoryEnabled = undefined;
274
- state.previousActiveRepo = undefined;
275
- state.ownsActiveRepoOrder = false;
276
- state.managedAdapters = [];
277
- state.lastSync = Date.now();
278
- state.error = '';
279
- await this.persist(false, state);
280
- this.adapter.log.info(`[NexoWatt stable updates] Disabled and previous policies restored (${reason})`);
281
- return this.statusFromNative({ eosNexoWattAutoUpdate: false, eosNexoWattAutoUpdateState: state });
282
- }
283
-
284
- private async writeAdapterPolicy(name: string, policy: NexoWattAutoUpdatePolicy | null): Promise<void> {
285
- const id = `system.adapter.${normalizedName(name)}`;
286
- const object = await this.adapter.getForeignObjectAsync(id);
287
- if (!object?.common) return;
288
- if (policy === null) delete object.common.automaticUpgrade;
289
- else object.common.automaticUpgrade = policy;
290
- await this.adapter.setForeignObjectAsync(id, object, { user: ADMIN_USER });
291
- }
292
-
293
- private async persist(enabled: boolean, state: PersistedState): Promise<void> {
294
- await this.adapter.extendForeignObjectAsync(this.instanceId, {
295
- native: { eosNexoWattAutoUpdate: enabled, eosNexoWattAutoUpdateState: state },
296
- }, { user: ADMIN_USER });
297
- }
298
-
299
- private scheduleRetry(): void {
300
- if (this.retryTimer) clearTimeout(this.retryTimer);
301
- this.retryTimer = setTimeout(() => void this.reconcile('retry'), 60_000);
302
- this.retryTimer.unref?.();
303
- }
304
-
305
- private scheduleRepositoryRefresh(repository: string, state: PersistedState): void {
306
- if (!this.adapter.sendToHostAsync || !this.adapter.host) return;
307
- if (this.refreshTimer) clearTimeout(this.refreshTimer);
308
- this.refreshTimer = setTimeout(() => {
309
- void this.adapter.sendToHostAsync?.(this.adapter.host!, 'getRepository', { repo: repository, update: true })
310
- .then(async () => {
311
- const current = await this.adapter.getForeignObjectAsync(this.instanceId);
312
- if (current?.native?.eosNexoWattAutoUpdate === false) return;
313
- state.lastRepositoryRefresh = Date.now();
314
- await this.persist(true, state);
315
- })
316
- .catch(error => this.adapter.log.warn(`[NexoWatt stable updates] Repository refresh failed: ${error instanceof Error ? error.message : error}`));
317
- }, REPOSITORY_REFRESH_DELAY_MS);
318
- this.refreshTimer.unref?.();
319
- }
320
- }
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const fs=require('node:fs');const path=require('node:path');const root=path.resolve(__dirname,'..');
4
- const read=rel=>fs.readFileSync(path.join(root,rel),'utf8');const fail=m=>{console.error(`[NexoWatt EOS cache update] ${m}`);process.exit(1)};const must=(c,m)=>{if(!c)fail(m)};
5
- const source=read('src/lib/web.ts'),built=read('build/lib/web.js'),index=read('adminWww/index.html'),dev=read('src-admin/index.html'),watch=read('adminWww/js/eos-release-watch.js'),pkg=JSON.parse(read('package.json')),info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json'));
6
- const release=pkg.version,tag=String(info.shellCacheTag||info.shellCacheVersion),shell=Number(info.shellCacheVersion);
7
- for(const code of [source,built])for(const marker of ['isEosCurrentAsset','applyNoStoreHeaders','Surrogate-Control','this.indexHTML = await this.prepareIndex'])must(code.includes(marker),`server marker missing: ${marker}`);
8
- for(const marker of [`const RELEASE = '${release}'`,'nexowatt.eos.loadedRelease','cache: \'no-store\'','window.location.replace','navigator.serviceWorker?.getRegistrations'])must(watch.includes(marker),`watcher marker missing: ${marker}`);
9
- for(const html of [index,dev])for(const marker of [`eos-release-watch.js?v=${tag}`,`nexowatt-stable-v${shell}.js?v=${tag}`,`nexowatt-native-shell.css?v=${tag}`])must(html.includes(marker),`index marker missing: ${marker}`);
10
- must(index.includes(`hostInit-v84.js?v=${tag}`)&&index.includes(`index-CQZugZ1z-v84.js?v=${tag}`),'productive module entry cache tags missing');
11
- must(!/\?v=(?:97|98|99)(?:["'])/.test(index),'stale product cache tag remains');
12
- console.log('[NexoWatt EOS cache update] OK (no-store entrypoints and release watcher)');
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const assert=require('node:assert/strict');
4
- const {isEosSameOriginRequest}=require('../build/lib/eosRequestSecurity.js');
5
- assert.equal(isEosSameOriginRequest({host:'192.168.10.124:8081'}),true);
6
- assert.equal(isEosSameOriginRequest({origin:'http://192.168.10.124:8081',host:'192.168.10.124:8081','sec-fetch-site':'same-origin'}),true);
7
- assert.equal(isEosSameOriginRequest({origin:'https://eos.example.test',host:'proxy:443','x-forwarded-host':'eos.example.test','sec-fetch-site':'same-site'}),true);
8
- assert.equal(isEosSameOriginRequest({origin:'http://192.168.10.125:8081',host:'192.168.10.124:8081','sec-fetch-site':'same-origin'}),false);
9
- assert.equal(isEosSameOriginRequest({origin:'http://192.168.10.124:8081',host:'192.168.10.124:8081','sec-fetch-site':'cross-site'}),false);
10
- assert.equal(isEosSameOriginRequest({origin:'file:///tmp/eos',host:'192.168.10.124:8081'}),false);
11
- console.log('[NexoWatt EOS request security] OK (LAN, proxy and cross-site guard)');
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const fs=require('node:fs'),path=require('node:path');const root=path.resolve(__dirname,'..');const read=r=>fs.readFileSync(path.join(root,r),'utf8');const must=(v,m)=>{if(!v){console.error(`[NexoWatt EOS stable v7100] ${m}`);process.exit(1)}};
4
- const p=JSON.parse(read('package.json')),io=JSON.parse(read('io-package.json')),info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json')),css=read('adminWww/css/nexowatt-native-shell.css'),stable=read('adminWww/static/js/nexowatt-stable-v7100.js'),login=read('adminWww/static/js/nexowatt-stable-login.js'),boot=read('adminWww/js/eos-role-bootstrap.js'),password=read('src/lib/eosPassword.ts'),web=read('src/lib/web.ts'),index=read('adminWww/index.html'),auto=read('src/lib/eosAutoUpdate.ts');
5
- must(p.version==='7.10.0'&&io.version==='7.10.0'&&io.common.version==='7.10.0'&&info.version==='7.10.0','version drift');
6
- must(info.shellCacheVersion===7100&&info.shellCacheTag==='7100','cache tag drift');
7
- must(index.includes('nexowatt-stable-v7100.js?v=7100')&&index.includes('eos-auto-update.js?v=7100'),'active assets missing');
8
- must(stable.includes('ensureCockpitScroll')&&stable.includes('disableCockpitScroll')&&stable.includes("overflowY: 'scroll'")&&stable.includes("paper.addEventListener('wheel'")&&stable.includes('var(--nx-content-top, 154px)'),'scroll runtime missing or route cleanup incomplete');
9
- must(css.includes('restored clean login geometry')&&css.includes('static labels above login fields')&&css.includes('position: static !important')&&css.includes('overflow-y: scroll !important'),'login/scroll CSS missing');
10
- must(login.includes('MuiInputLabel-shrink')&&login.includes('normalizeGeometry')&&login.includes("removeAttribute('aria-hidden')"),'login static-label runtime missing');
11
- must(boot.includes("const card = form?.closest?.('.MuiPaper-root')")&&!boot.includes("closest?.('.MuiPaper-root,form,main,section')"),'login Paper ownership incorrect');
12
- must(boot.includes('waitForPasswordSocket')&&boot.includes('socket.changePassword')&&boot.includes('passwordAlreadyWritten')&&boot.includes('postWithTimeout'),'native user-editor password path missing');
13
- must(password.includes('apiTarget: normalized.userId')&&password.includes('apiTarget: normalized.userName')&&password.includes('verifyAfterWrite')&&password.includes('verifyEosUserPasswordCredential'),'password compatibility/verification missing');
14
- must(web.includes('EosPasswordSetupTicket')&&web.includes('passwordAlreadyWritten')&&web.includes('verifyEosUserPasswordCredential'),'setup ticket/finalization path missing');
15
- must(!/issueEosPasswordSetupTicket[\s\S]{0,600}invalidateEosPasswordSetupTicketsForUser\(userId\)/.test(web),'policy refresh invalidates first-login ticket');
16
- must(auto.includes('eosNexoWattAutoUpdate !== false')&&auto.includes("const POLICY: NexoWattAutoUpdatePolicy = 'major'"),'NexoWatt stable auto-update default missing');
17
- console.log('[NexoWatt EOS stable v7100] OK');