iobroker.eos-admin 7.10.1 → 7.10.3
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/INSTALL_TEST_V7.10.3.md +7 -0
- package/MERGE_UPDATE_README_V7.10.3.md +3 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +16 -16
- package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +1412 -0
- package/PUBLISH_STABLE_V7.10.3.md +10 -0
- package/README.md +4 -4
- package/README_STABILITY_V7.10.3.md +5 -0
- package/RELEASE_ACCEPTANCE_V7.10.3.md +3 -0
- package/RELEASE_NOTES_V7.10.3.md +7 -0
- package/adminWww/css/eos-auto-update.css +4 -3
- package/adminWww/index.html +2 -2
- package/adminWww/js/eos-auto-update.js +127 -33
- package/build/lib/eosAutoUpdate.js +418 -20
- package/build/lib/eosAutoUpdate.js.map +1 -0
- package/build/lib/eosRequestSecurity.js +4 -1
- package/build/lib/eosRequestSecurity.js.map +1 -0
- package/build/lib/web.js +40 -9
- package/build/lib/web.js.map +1 -1
- package/io-package.json +65 -5
- package/package.json +24 -16
- package/tools/nexowatt-auto-update-placement-selftest.cjs +46 -0
- package/tools/nexowatt-auto-update-selftest.cjs +144 -32
- package/tools/nexowatt-backend-runtime-selftest.cjs +147 -0
- package/tools/nexowatt-merge-update-selftest.cjs +7 -1
- package/tools/nexowatt-prebuilt-release-selftest.cjs +292 -0
- package/tools/nexowatt-stable-v7103-selftest.cjs +61 -0
- package/tools/nexowatt-validate-package.cjs +45 -12
- package/INSTALL_TEST_V7.10.1.md +0 -10
- package/MERGE_UPDATE_README_V7.10.1.md +0 -10
- package/PUBLISH_STABLE_V7.10.1.md +0 -10
- package/README_STABILITY_V7.10.1.md +0 -10
- package/RELEASE_ACCEPTANCE_V7.10.1.md +0 -10
- package/RELEASE_NOTES_V7.10.1.md +0 -10
- package/adminWww/js/eos-release-watch.js +0 -77
- package/build/lib/eosPassword.js +0 -72
- package/build/lib/nexowattStableAuth.js +0 -74
- package/tools/nexowatt-stable-v7101-selftest.cjs +0 -15
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { spawnSync } = require('node:child_process');
|
|
8
|
+
|
|
9
|
+
const root = path.resolve(__dirname, '..');
|
|
10
|
+
const manifestFile = path.join(root, 'NEXOWATT_EOS_PREBUILT_MANIFEST.json');
|
|
11
|
+
const packageFile = path.join(root, 'package.json');
|
|
12
|
+
const trackedRoots = [
|
|
13
|
+
'src',
|
|
14
|
+
'build',
|
|
15
|
+
'src-admin/src',
|
|
16
|
+
'src-admin/public',
|
|
17
|
+
'adminWww',
|
|
18
|
+
'admin',
|
|
19
|
+
'public',
|
|
20
|
+
'tools',
|
|
21
|
+
];
|
|
22
|
+
const trackedFiles = [
|
|
23
|
+
'tasks.mts',
|
|
24
|
+
'tsconfig.json',
|
|
25
|
+
'tsconfig.build.json',
|
|
26
|
+
];
|
|
27
|
+
const expectedPrepublishOnly =
|
|
28
|
+
'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime && npm run check:eos-publish-channel && npm run check:eos-package && npm run check:eos-stability';
|
|
29
|
+
const expectedPrepack =
|
|
30
|
+
'node tools/nexowatt-sync-release-version.cjs --quiet && node tools/nexowatt-ensure-release-defaults.cjs && node tools/nexowatt-clean-legacy-runtime.cjs --quiet && node tools/nexowatt-prebuilt-release-selftest.cjs';
|
|
31
|
+
|
|
32
|
+
function normalize(relative) {
|
|
33
|
+
return relative.replace(/\\/g, '/');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function walk(relative, output) {
|
|
37
|
+
const absolute = path.join(root, relative);
|
|
38
|
+
if (!fs.existsSync(absolute)) return;
|
|
39
|
+
const stat = fs.lstatSync(absolute);
|
|
40
|
+
if (stat.isSymbolicLink()) {
|
|
41
|
+
throw new Error(`symbolic links are not allowed in the sealed release: ${normalize(relative)}`);
|
|
42
|
+
}
|
|
43
|
+
if (stat.isFile()) {
|
|
44
|
+
output.push(normalize(relative));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!stat.isDirectory()) return;
|
|
48
|
+
for (const name of fs.readdirSync(absolute).sort()) {
|
|
49
|
+
walk(path.join(relative, name), output);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function collectTrackedFiles() {
|
|
54
|
+
const files = [];
|
|
55
|
+
for (const relative of trackedRoots) walk(relative, files);
|
|
56
|
+
for (const relative of trackedFiles) {
|
|
57
|
+
const absolute = path.join(root, relative);
|
|
58
|
+
if (!fs.existsSync(absolute) || !fs.statSync(absolute).isFile()) {
|
|
59
|
+
throw new Error(`tracked release input is missing: ${relative}`);
|
|
60
|
+
}
|
|
61
|
+
files.push(normalize(relative));
|
|
62
|
+
}
|
|
63
|
+
return [...new Set(files)].sort();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sha256(relative) {
|
|
67
|
+
return crypto.createHash('sha256').update(fs.readFileSync(path.join(root, relative))).digest('hex');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createManifest() {
|
|
71
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
72
|
+
const files = collectTrackedFiles();
|
|
73
|
+
const hashes = {};
|
|
74
|
+
for (const relative of files) hashes[relative] = sha256(relative);
|
|
75
|
+
return {
|
|
76
|
+
format: 1,
|
|
77
|
+
package: pkg.name,
|
|
78
|
+
version: pkg.version,
|
|
79
|
+
algorithm: 'sha256',
|
|
80
|
+
fileCount: files.length,
|
|
81
|
+
trackedRoots,
|
|
82
|
+
trackedFiles,
|
|
83
|
+
files: hashes,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function writeManifest() {
|
|
88
|
+
const manifest = createManifest();
|
|
89
|
+
fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
90
|
+
console.log(`[NexoWatt EOS prebuilt release] sealed ${manifest.fileCount} source/runtime file(s) for ${manifest.version}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveLocal(fromFile, specifier) {
|
|
94
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
95
|
+
const candidates = [base, `${base}.js`, `${base}.json`, path.join(base, 'index.js')];
|
|
96
|
+
if (fs.existsSync(base) && fs.statSync(base).isDirectory()) {
|
|
97
|
+
const nestedPackage = path.join(base, 'package.json');
|
|
98
|
+
if (fs.existsSync(nestedPackage)) {
|
|
99
|
+
try {
|
|
100
|
+
const value = JSON.parse(fs.readFileSync(nestedPackage, 'utf8'));
|
|
101
|
+
if (value.main) candidates.unshift(path.resolve(base, value.main));
|
|
102
|
+
} catch {
|
|
103
|
+
// The normal package validator reports malformed JSON in detail.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return candidates.find(candidate => fs.existsSync(candidate) && fs.statSync(candidate).isFile()) || null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function inspectRuntimeClosure(failures) {
|
|
111
|
+
const entry = path.join(root, 'build', 'main.js');
|
|
112
|
+
const visited = new Set();
|
|
113
|
+
const runtimeFiles = new Set();
|
|
114
|
+
|
|
115
|
+
function inspect(file) {
|
|
116
|
+
const absolute = path.resolve(file);
|
|
117
|
+
if (visited.has(absolute)) return;
|
|
118
|
+
visited.add(absolute);
|
|
119
|
+
runtimeFiles.add(absolute);
|
|
120
|
+
if (!fs.existsSync(absolute)) {
|
|
121
|
+
failures.push(`missing runtime file ${normalize(path.relative(root, absolute))}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!absolute.endsWith('.js')) return;
|
|
125
|
+
|
|
126
|
+
const parsed = spawnSync(process.execPath, ['--check', absolute], {
|
|
127
|
+
encoding: 'utf8',
|
|
128
|
+
windowsHide: true,
|
|
129
|
+
});
|
|
130
|
+
if (parsed.status !== 0) {
|
|
131
|
+
failures.push(
|
|
132
|
+
`${normalize(path.relative(root, absolute))} does not parse: ${(parsed.stderr || parsed.stdout || '').trim()}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const text = fs.readFileSync(absolute, 'utf8');
|
|
137
|
+
const patterns = [
|
|
138
|
+
/\brequire\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
139
|
+
/\brequire\.resolve\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
140
|
+
/\bimport\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
141
|
+
];
|
|
142
|
+
for (const pattern of patterns) {
|
|
143
|
+
pattern.lastIndex = 0;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = pattern.exec(text))) {
|
|
146
|
+
const specifier = match[1];
|
|
147
|
+
if (!specifier.startsWith('.')) continue;
|
|
148
|
+
const target = resolveLocal(absolute, specifier);
|
|
149
|
+
if (!target) {
|
|
150
|
+
failures.push(`${normalize(path.relative(root, absolute))} -> ${specifier} cannot be resolved`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
inspect(target);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
inspect(entry);
|
|
159
|
+
for (const required of [
|
|
160
|
+
'build/main.js',
|
|
161
|
+
'build/lib/web.js',
|
|
162
|
+
'build/lib/eosAutoUpdate.js',
|
|
163
|
+
'build/lib/eosRequestSecurity.js',
|
|
164
|
+
]) {
|
|
165
|
+
if (!runtimeFiles.has(path.join(root, required))) {
|
|
166
|
+
failures.push(`entrypoint closure does not include required module ${required}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return runtimeFiles.size;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function verifyManifest(failures) {
|
|
173
|
+
if (!fs.existsSync(manifestFile)) {
|
|
174
|
+
failures.push('NEXOWATT_EOS_PREBUILT_MANIFEST.json is missing');
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
let manifest;
|
|
178
|
+
try {
|
|
179
|
+
manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
failures.push(`prebuilt manifest is invalid JSON: ${error.message}`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
185
|
+
if (manifest.format !== 1) failures.push(`unsupported prebuilt manifest format ${manifest.format}`);
|
|
186
|
+
if (manifest.package !== pkg.name) failures.push(`prebuilt manifest package ${manifest.package} does not match ${pkg.name}`);
|
|
187
|
+
if (manifest.version !== pkg.version) failures.push(`prebuilt manifest version ${manifest.version} does not match ${pkg.version}`);
|
|
188
|
+
if (manifest.algorithm !== 'sha256') failures.push(`prebuilt manifest algorithm must be sha256, got ${manifest.algorithm}`);
|
|
189
|
+
|
|
190
|
+
let currentFiles = [];
|
|
191
|
+
try {
|
|
192
|
+
currentFiles = collectTrackedFiles();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
failures.push(error.message);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
const sealedFiles = Object.keys(manifest.files || {}).sort();
|
|
198
|
+
const currentSet = new Set(currentFiles);
|
|
199
|
+
const sealedSet = new Set(sealedFiles);
|
|
200
|
+
for (const relative of currentFiles) {
|
|
201
|
+
if (!sealedSet.has(relative)) failures.push(`unsealed release file: ${relative}`);
|
|
202
|
+
}
|
|
203
|
+
for (const relative of sealedFiles) {
|
|
204
|
+
if (!currentSet.has(relative)) failures.push(`sealed release file is missing: ${relative}`);
|
|
205
|
+
}
|
|
206
|
+
for (const relative of sealedFiles) {
|
|
207
|
+
if (!currentSet.has(relative)) continue;
|
|
208
|
+
const actual = sha256(relative);
|
|
209
|
+
if (actual !== manifest.files[relative]) failures.push(`sealed release file changed after validation: ${relative}`);
|
|
210
|
+
}
|
|
211
|
+
if (manifest.fileCount !== sealedFiles.length) {
|
|
212
|
+
failures.push(`prebuilt manifest fileCount ${manifest.fileCount} does not match ${sealedFiles.length}`);
|
|
213
|
+
}
|
|
214
|
+
return sealedFiles.length;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function verifyLifecycle(failures) {
|
|
218
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
219
|
+
const scripts = pkg.scripts || {};
|
|
220
|
+
if (scripts.prepublishOnly !== expectedPrepublishOnly) {
|
|
221
|
+
failures.push('prepublishOnly is not the canonical dependency-free direct-publish workflow');
|
|
222
|
+
}
|
|
223
|
+
if (scripts.prepack !== expectedPrepack) {
|
|
224
|
+
failures.push('prepack is not the canonical dependency-free prebuilt-artifact verifier');
|
|
225
|
+
}
|
|
226
|
+
if (scripts['check:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs') {
|
|
227
|
+
failures.push('check:eos-prebuilt-release script is missing or incorrect');
|
|
228
|
+
}
|
|
229
|
+
if (scripts['seal:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs --write') {
|
|
230
|
+
failures.push('seal:eos-prebuilt-release script is missing or incorrect');
|
|
231
|
+
}
|
|
232
|
+
for (const [name, command] of [
|
|
233
|
+
['prepublishOnly', scripts.prepublishOnly || ''],
|
|
234
|
+
['prepack', scripts.prepack || ''],
|
|
235
|
+
]) {
|
|
236
|
+
for (const forbidden of [
|
|
237
|
+
/(?:^|\s)tsc(?:\s|$)/i,
|
|
238
|
+
/(?:^|\s)tsx(?:\s|$)/i,
|
|
239
|
+
/build:backend/i,
|
|
240
|
+
/build:frontend/i,
|
|
241
|
+
/(?:^|\s)npx(?:\s|$)/i,
|
|
242
|
+
/npm\s+(?:i|install|ci)(?:\s|$)/i,
|
|
243
|
+
]) {
|
|
244
|
+
if (forbidden.test(command)) failures.push(`${name} must not require local development dependencies: ${command}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const backendRuntimeSelftestFile = path.join(root, 'tools', 'nexowatt-backend-runtime-selftest.cjs');
|
|
248
|
+
if (!fs.existsSync(backendRuntimeSelftestFile)) {
|
|
249
|
+
failures.push('tools/nexowatt-backend-runtime-selftest.cjs is missing');
|
|
250
|
+
} else {
|
|
251
|
+
const backendRuntimeSelftest = fs.readFileSync(backendRuntimeSelftestFile, 'utf8');
|
|
252
|
+
if (!backendRuntimeSelftest.includes('dependency-free on Windows')) {
|
|
253
|
+
failures.push('backend runtime selftest is missing the cross-platform publish marker');
|
|
254
|
+
}
|
|
255
|
+
if (/const\s+npm\s*=\s*process\.platform|spawnSync\(\s*npm\s*,|['"]npm\.cmd['"]/.test(backendRuntimeSelftest)) {
|
|
256
|
+
failures.push('backend runtime selftest must not spawn a nested npm process');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const packageFiles = new Set((pkg.files || []).map(value => String(value).replace(/\\/g, '/')));
|
|
260
|
+
if (![...packageFiles].some(value => value.replace(/\/+$/, '') === 'build')) {
|
|
261
|
+
failures.push('package.json files must include the complete build directory');
|
|
262
|
+
}
|
|
263
|
+
for (const required of [
|
|
264
|
+
'tools/nexowatt-prebuilt-release-selftest.cjs',
|
|
265
|
+
'NEXOWATT_EOS_PREBUILT_MANIFEST.json',
|
|
266
|
+
]) {
|
|
267
|
+
if (!packageFiles.has(required)) failures.push(`package.json files is missing ${required}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (process.argv.includes('--write')) {
|
|
272
|
+
try {
|
|
273
|
+
writeManifest();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error(`[NexoWatt EOS prebuilt release] ${error instanceof Error ? error.message : String(error)}`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const failures = [];
|
|
282
|
+
verifyLifecycle(failures);
|
|
283
|
+
const sealedCount = verifyManifest(failures);
|
|
284
|
+
const runtimeCount = inspectRuntimeClosure(failures);
|
|
285
|
+
|
|
286
|
+
if (failures.length) {
|
|
287
|
+
console.error(`[NexoWatt EOS prebuilt release] ${failures.length} failure(s):\n${failures.join('\n')}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
console.log(
|
|
291
|
+
`[NexoWatt EOS prebuilt release] OK (${sealedCount} sealed source/runtime files; ${runtimeCount} backend runtime files; no tsc/tsx required for npm publish)`,
|
|
292
|
+
);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const root = path.resolve(__dirname, '..');
|
|
7
|
+
const read = relative => fs.readFileSync(path.join(root, relative), 'utf8');
|
|
8
|
+
const json = relative => JSON.parse(read(relative));
|
|
9
|
+
const must = (value, message) => {
|
|
10
|
+
if (!value) {
|
|
11
|
+
console.error(`[NexoWatt EOS stable v7103] ${message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const pkg = json('package.json');
|
|
17
|
+
const io = json('io-package.json');
|
|
18
|
+
const info = json('NEXOWATT_EOS_BUILD_INFO.json');
|
|
19
|
+
const main = read('src/main.ts');
|
|
20
|
+
const web = read('src/lib/web.ts');
|
|
21
|
+
const boot = read('adminWww/js/eos-role-bootstrap.js');
|
|
22
|
+
const role = read('adminWww/js/eos-role-ui.js');
|
|
23
|
+
const css = read('adminWww/css/nexowatt-native-shell.css');
|
|
24
|
+
const index = read('adminWww/index.html');
|
|
25
|
+
const ems = read('adminWww/js/eos-ems-overview.js');
|
|
26
|
+
const intro = read('src-admin/src/tabs/Intro.tsx');
|
|
27
|
+
const bundle = read('adminWww/assets/Intro-DkwRiz1n-v84.js');
|
|
28
|
+
const stable = read('adminWww/static/js/nexowatt-stable-v7101.js');
|
|
29
|
+
const autoBackend = read('src/lib/eosAutoUpdate.ts');
|
|
30
|
+
const autoUi = read('adminWww/js/eos-auto-update.js');
|
|
31
|
+
const autoCss = read('adminWww/css/eos-auto-update.css');
|
|
32
|
+
const autoTag = String(info.autoUpdateCacheTag || info.autoUpdateCacheVersion || info.shellCacheTag || info.shellCacheVersion);
|
|
33
|
+
|
|
34
|
+
must(pkg.version === '7.10.3' && io.version === '7.10.3' && io.common.version === '7.10.3' && info.version === '7.10.3', 'version drift');
|
|
35
|
+
must(io.native.eosRequireFirstLoginPassword === false && io.native.eosRequireFirstLoginPasswordChange === false, 'forced first password still enabled');
|
|
36
|
+
must(!index.includes('eos-account-management.js'), 'custom reset UI still loaded');
|
|
37
|
+
must(!boot.includes('installIntegratedFirstLogin(base);') && !boot.includes('showFirstLoginPassword(resolved, base);'), 'forced first-login UI still active');
|
|
38
|
+
must(main.includes('standardPasswordMode: true') && main.includes('nexowattPasswordChangeRequired: false'), 'account migration missing');
|
|
39
|
+
must(role.includes("route === 'tab-users'") && main.includes("command === 'changePassword'"), 'role-aware native password editing missing');
|
|
40
|
+
must(css.includes('persistent native overview scrolling') && stable.includes("overflow-y','scroll"), 'persistent scroll missing');
|
|
41
|
+
must(intro.includes('eos-overview-edit-controls') && bundle.includes('eos-overview-edit-controls'), 'edit control not in bottom grid');
|
|
42
|
+
must(!ems.includes('Vollständige EMS-Diagnose öffnen'), 'unsupported EMS link remains');
|
|
43
|
+
|
|
44
|
+
// 7.10.2 runtime and package hotfix baseline remains mandatory.
|
|
45
|
+
must(autoBackend.includes("const ENABLED_STATE_ID = 'info.nexowattStableUpdatesEnabled'"), 'restart-safe enabled state missing');
|
|
46
|
+
must(autoBackend.includes("const STATUS_STATE_ID = 'info.nexowattStableUpdatesState'"), 'restart-safe status state missing');
|
|
47
|
+
must(autoBackend.includes('const STARTUP_RECONCILE_DELAY_MS = 30_000'), 'startup grace period missing');
|
|
48
|
+
must(!autoBackend.includes('extendForeignObjectAsync(this.instanceId'), 'runtime still writes the running instance native configuration');
|
|
49
|
+
must(web.includes('this.nexowattStableUpdateManager?.stop();'), 'auto-update manager is not stopped with the web server');
|
|
50
|
+
must(fs.existsSync(path.join(root, 'build/lib/eosRequestSecurity.js')), 'eosRequestSecurity runtime module missing');
|
|
51
|
+
|
|
52
|
+
// 7.10.3 placement acceptance: System Settings only.
|
|
53
|
+
must(index.includes(`eos-auto-update.js?v=${autoTag}`), 'auto-update JavaScript cache key missing');
|
|
54
|
+
must(index.includes(`eos-auto-update.css?v=${autoTag}`), 'auto-update CSS cache key missing');
|
|
55
|
+
must(autoUi.includes('[role="dialog"][aria-labelledby="system-settings-dialog-title"]'), 'System Settings selector missing');
|
|
56
|
+
must(autoUi.includes("root.dataset.context = 'system-settings'"), 'System Settings context missing');
|
|
57
|
+
must(autoUi.includes('mount.content.insertBefore(root, mount.appBar.nextSibling)'), 'card is not placed below System Settings header');
|
|
58
|
+
must(!autoUi.includes('introActive') && !autoUi.includes('eos-native-overview-hero') && !autoUi.includes('eos-ems-overview-runtime'), 'global overview/module placement remains');
|
|
59
|
+
must(autoCss.includes('visible only inside the System Settings dialog'), 'System Settings CSS ownership missing');
|
|
60
|
+
|
|
61
|
+
console.log('[NexoWatt EOS stable v7103] OK (v7102 crash/package baseline plus System Settings-only update control)');
|
|
@@ -11,6 +11,8 @@ const fail = msg => {
|
|
|
11
11
|
const read = file => fs.readFileSync(path.join(root, file), 'utf8');
|
|
12
12
|
const readJson = file => JSON.parse(read(file));
|
|
13
13
|
const exists = file => fs.existsSync(path.join(root, file));
|
|
14
|
+
const canonicalPrepublishOnly = 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime && npm run check:eos-publish-channel && npm run check:eos-package && npm run check:eos-stability';
|
|
15
|
+
const canonicalPrepack = 'node tools/nexowatt-sync-release-version.cjs --quiet && node tools/nexowatt-ensure-release-defaults.cjs && node tools/nexowatt-clean-legacy-runtime.cjs --quiet && node tools/nexowatt-prebuilt-release-selftest.cjs';
|
|
14
16
|
|
|
15
17
|
const pkg = readJson('package.json');
|
|
16
18
|
const pkgLock = readJson('package-lock.json');
|
|
@@ -41,8 +43,18 @@ if (pkg.scripts['test:eos-version-sync'] !== 'node tools/nexowatt-version-sync-s
|
|
|
41
43
|
if (pkg.scripts['verify:eos-merge'] !== 'node tools/nexowatt-merge-update-selftest.cjs') fail('merge update selftest script is missing or incorrect');
|
|
42
44
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-version-sync-selftest.cjs')) fail('version sync selftest is not part of check:eos-stability');
|
|
43
45
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-merge-update-selftest.cjs')) fail('merge update selftest is not part of check:eos-stability');
|
|
44
|
-
if (pkg.scripts['check:eos-stable-
|
|
45
|
-
if (
|
|
46
|
+
if (pkg.scripts['check:eos-stable-v7103'] !== 'node tools/nexowatt-stable-v7103-selftest.cjs') fail('stable v7103 selftest script is missing or incorrect');
|
|
47
|
+
if (pkg.scripts['test:eos-auto-update-placement'] !== 'node tools/nexowatt-auto-update-placement-selftest.cjs') fail('auto-update placement selftest script is missing or incorrect');
|
|
48
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-auto-update-placement-selftest.cjs')) fail('auto-update placement selftest is not part of check:eos-stability');
|
|
49
|
+
if (pkg.scripts.prepublishOnly !== canonicalPrepublishOnly) fail('prepublishOnly must use the dependency-free direct-publish workflow');
|
|
50
|
+
if (/\b(?:tsc|tsx)\b|build:(?:backend|frontend)|\bnpx\b|npm\s+(?:i|install|ci)\b/i.test(pkg.scripts.prepublishOnly || '')) fail('prepublishOnly must not require local development dependencies');
|
|
51
|
+
if (pkg.scripts['check:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs') fail('prebuilt release selftest script is missing or incorrect');
|
|
52
|
+
if (pkg.scripts['seal:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs --write') fail('prebuilt release sealing script is missing or incorrect');
|
|
53
|
+
const backendRuntimeSelftest = read('tools/nexowatt-backend-runtime-selftest.cjs');
|
|
54
|
+
if (!backendRuntimeSelftest.includes('dependency-free on Windows')) fail('backend runtime selftest is missing the cross-platform publish marker');
|
|
55
|
+
if (/const\s+npm\s*=\s*process\.platform|spawnSync\(\s*npm\s*,|['"]npm\.cmd['"]/.test(backendRuntimeSelftest)) fail('backend runtime selftest must not spawn a nested npm process');
|
|
56
|
+
if (!(pkg.files || []).some(value => String(value).replace(/\\/g, '/').replace(/\/+$/, '') === 'build')) fail('package.json files must include the complete build directory');
|
|
57
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-prebuilt-release-selftest.cjs')) fail('prebuilt release selftest is not part of check:eos-stability');
|
|
46
58
|
if (pkg.scripts['precheck:eos-package'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-package must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
47
59
|
if (pkg.scripts['precheck:eos-stability'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-stability must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
48
60
|
if (pkg.scripts['test:eos-publish-channel'] !== 'node tools/nexowatt-publish-channel-selftest.cjs') fail('publish channel selftest script is missing');
|
|
@@ -53,6 +65,9 @@ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flo
|
|
|
53
65
|
if (io.native?.auth !== true) fail('fresh sales installations must default to authenticated access');
|
|
54
66
|
if (io.native?.eosHideLegacyBackupFromNonAdmins !== true) fail('internal BackItUp reserve must default to Admin/Service visibility');
|
|
55
67
|
if (io.native?.nexowattHideLegacyBackupFromNonAdmins !== true) fail('NexoWatt backup visibility compatibility flag must default to true');
|
|
68
|
+
const instanceObjectIds = new Set((io.instanceObjects || []).map(object => object?._id));
|
|
69
|
+
if (!instanceObjectIds.has('info.nexowattStableUpdatesEnabled')) fail('restart-safe auto-update enabled state is missing');
|
|
70
|
+
if (!instanceObjectIds.has('info.nexowattStableUpdatesState')) fail('restart-safe auto-update runtime state is missing');
|
|
56
71
|
for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
|
|
57
72
|
if (JSON.stringify(pkgLock.packages?.['']?.[key]) !== JSON.stringify(pkg[key])) {
|
|
58
73
|
fail(`package-lock root ${key} must match package.json`);
|
|
@@ -114,23 +129,27 @@ for (const file of [
|
|
|
114
129
|
'src-admin/src/components/Drawer.tsx',
|
|
115
130
|
'src-admin/src/components/DrawerItem.tsx',
|
|
116
131
|
'admin/admin.svg',
|
|
132
|
+
'build/lib/eosRequestSecurity.js',
|
|
117
133
|
'LICENSE',
|
|
118
134
|
'NEXOWATT_PROPRIETARY_LICENSE.md',
|
|
119
135
|
'THIRD_PARTY_NOTICES.md',
|
|
120
|
-
'README_STABILITY_V7.10.
|
|
121
|
-
'RELEASE_NOTES_V7.10.
|
|
122
|
-
'PUBLISH_STABLE_V7.10.
|
|
123
|
-
'INSTALL_TEST_V7.10.
|
|
124
|
-
'RELEASE_ACCEPTANCE_V7.10.
|
|
136
|
+
'README_STABILITY_V7.10.3.md',
|
|
137
|
+
'RELEASE_NOTES_V7.10.3.md',
|
|
138
|
+
'PUBLISH_STABLE_V7.10.3.md',
|
|
139
|
+
'INSTALL_TEST_V7.10.3.md',
|
|
140
|
+
'RELEASE_ACCEPTANCE_V7.10.3.md',
|
|
125
141
|
'MERGE_UPDATE.ps1',
|
|
126
142
|
'MERGE_UPDATE.cmd',
|
|
127
|
-
'MERGE_UPDATE_README_V7.10.
|
|
143
|
+
'MERGE_UPDATE_README_V7.10.3.md',
|
|
128
144
|
'tools/nexowatt-patch-built-frontend.cjs',
|
|
129
145
|
'tools/nexowatt-native-shell-selftest.cjs',
|
|
130
146
|
'tools/nexowatt-clean-legacy-runtime.cjs',
|
|
131
147
|
'tools/nexowatt-runtime-cleanup-selftest.cjs',
|
|
132
148
|
'tools/nexowatt-esm-syntax-selftest.cjs',
|
|
133
149
|
'tools/nexowatt-import-integrity-selftest.cjs',
|
|
150
|
+
'tools/nexowatt-backend-runtime-selftest.cjs',
|
|
151
|
+
'tools/nexowatt-prebuilt-release-selftest.cjs',
|
|
152
|
+
'NEXOWATT_EOS_PREBUILT_MANIFEST.json',
|
|
134
153
|
'tools/nexowatt-entrypoint-smoke-selftest.cjs',
|
|
135
154
|
'tools/nexowatt-role-access-selftest.cjs',
|
|
136
155
|
'tools/nexowatt-first-login-selftest.cjs',
|
|
@@ -142,9 +161,14 @@ for (const file of [
|
|
|
142
161
|
'tools/nexowatt-sync-release-version.cjs',
|
|
143
162
|
'tools/nexowatt-version-sync-selftest.cjs',
|
|
144
163
|
'tools/nexowatt-merge-update-selftest.cjs',
|
|
145
|
-
'tools/nexowatt-stable-
|
|
164
|
+
'tools/nexowatt-stable-v7103-selftest.cjs',
|
|
146
165
|
'tools/nexowatt-password-write-selftest.cjs',
|
|
147
166
|
'tools/nexowatt-account-password-flow-selftest.cjs',
|
|
167
|
+
'tools/nexowatt-auto-update-placement-selftest.cjs',
|
|
168
|
+
'adminWww/js/eos-auto-update.js',
|
|
169
|
+
'adminWww/css/eos-auto-update.css',
|
|
170
|
+
'src-admin/public/js/eos-auto-update.js',
|
|
171
|
+
'src-admin/public/css/eos-auto-update.css',
|
|
148
172
|
'tools/nexowatt-ems-overview-selftest.cjs',
|
|
149
173
|
'tools/nexowatt-ui-overview-runtime-selftest.cjs',
|
|
150
174
|
'adminWww/js/eos-ems-overview.js',
|
|
@@ -174,6 +198,7 @@ const runtime = buildInfo.runtimeEntry;
|
|
|
174
198
|
const runtimeNumber = Number(String(runtime).replace(/^v/, ''));
|
|
175
199
|
const shellCache = Number(buildInfo.shellCacheVersion ?? buildInfo.nativeShellVersion ?? runtimeNumber);
|
|
176
200
|
const shellTag = String(buildInfo.shellCacheTag || shellCache);
|
|
201
|
+
const autoUpdateTag = String(buildInfo.autoUpdateCacheTag || buildInfo.autoUpdateCacheVersion || shellTag);
|
|
177
202
|
if (!runtime || !Number.isFinite(runtimeNumber)) fail(`invalid runtimeEntry ${runtime}`);
|
|
178
203
|
if (!Number.isFinite(shellCache)) fail(`invalid shellCacheVersion ${buildInfo.shellCacheVersion}`);
|
|
179
204
|
for (const marker of [
|
|
@@ -189,7 +214,8 @@ if (!index.includes(`eos-role-bootstrap.js?v=${shellTag}`)) fail('role bootstrap
|
|
|
189
214
|
if (!index.includes(`eos-branding-sanitizer.js?v=${shellTag}`)) fail('branding sanitizer cache key mismatch');
|
|
190
215
|
if (!index.includes(`eos-role-ui.js?v=${shellTag}`)) fail('role UI cache key mismatch');
|
|
191
216
|
if (index.includes('eos-account-management.js')) fail('custom account-management runtime must not be loaded in standard password mode');
|
|
192
|
-
if (!index.includes(`eos-auto-update.js?v=${
|
|
217
|
+
if (!index.includes(`eos-auto-update.js?v=${autoUpdateTag}`)) fail('auto-update JavaScript cache key mismatch');
|
|
218
|
+
if (!index.includes(`eos-auto-update.css?v=${autoUpdateTag}`)) fail('auto-update CSS cache key mismatch');
|
|
193
219
|
if (!index.includes(`eos-assistant.js?v=${shellTag}`)) fail('EOS Assist cache key mismatch');
|
|
194
220
|
if (!index.includes(`eos-ems-overview.js?v=${shellTag}`)) fail('EMS overview cache key mismatch');
|
|
195
221
|
if (!index.includes(`eos-ems-overview.css?v=${shellTag}`)) fail('EMS overview CSS cache key mismatch');
|
|
@@ -255,19 +281,26 @@ if (!mf.includes(`remoteEntry-${runtime}.js`) || !mf.includes(`index-D2ymscJA-${
|
|
|
255
281
|
const webBuild = read('build/lib/web.js');
|
|
256
282
|
if (!webBuild.includes('refreshLifetime: 60 * 60 * 24 * 7')) fail('build/lib/web.js must keep upstream-compatible refresh lifetime');
|
|
257
283
|
if (!webBuild.includes('Follow upstream admin semantics again')) fail('build/lib/web.js lacks session compatibility fix');
|
|
284
|
+
if (!exists('build/lib/eosRequestSecurity.js')) fail('build/lib/eosRequestSecurity.js is missing');
|
|
285
|
+
const autoUpdateSource = read('src/lib/eosAutoUpdate.ts');
|
|
286
|
+
if (autoUpdateSource.includes('extendForeignObjectAsync(this.instanceId')) fail('auto-update runtime must not write the running instance native configuration');
|
|
287
|
+
if (!autoUpdateSource.includes("const ENABLED_STATE_ID = 'info.nexowattStableUpdatesEnabled'")) fail('restart-safe auto-update state storage is missing');
|
|
258
288
|
const mainBuild = read('build/main.js');
|
|
259
289
|
if (!mainBuild.includes('v37 BackItUp/runtime-adapter compatibility')) fail('build/main.js lacks BackItUp compatibility guard');
|
|
260
290
|
|
|
261
291
|
if (!pkg.scripts['nexowatt:patch-built-frontend']) fail('missing nexowatt:patch-built-frontend script');
|
|
262
292
|
if (pkg.scripts['check:eos-esm'] !== 'node tools/nexowatt-esm-syntax-selftest.cjs') fail('check:eos-esm script is missing or incorrect');
|
|
263
293
|
if (pkg.scripts['check:eos-imports'] !== 'node tools/nexowatt-import-integrity-selftest.cjs') fail('check:eos-imports script is missing or incorrect');
|
|
294
|
+
if (pkg.scripts['check:eos-backend-runtime'] !== 'node tools/nexowatt-backend-runtime-selftest.cjs') fail('check:eos-backend-runtime script is missing or incorrect');
|
|
264
295
|
if (pkg.scripts['check:eos-entry'] !== 'node tools/nexowatt-entrypoint-smoke-selftest.cjs') fail('check:eos-entry script is missing or incorrect');
|
|
265
296
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-esm-syntax-selftest.cjs')) fail('ESM syntax selftest is not part of check:eos-stability');
|
|
266
297
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-import-integrity-selftest.cjs')) fail('import integrity selftest is not part of check:eos-stability');
|
|
298
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-backend-runtime-selftest.cjs')) fail('backend runtime selftest is not part of check:eos-stability');
|
|
267
299
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-entrypoint-smoke-selftest.cjs')) fail('entrypoint smoke selftest is not part of check:eos-stability');
|
|
268
300
|
if (pkg.scripts['clean:eos-runtime'] !== 'node tools/nexowatt-clean-legacy-runtime.cjs') fail('clean:eos-runtime script is missing or incorrect');
|
|
269
|
-
if (pkg.scripts.prepack !==
|
|
270
|
-
if (
|
|
301
|
+
if (pkg.scripts.prepack !== canonicalPrepack) fail('prepack must verify the sealed prebuilt artifact without tsc/tsx');
|
|
302
|
+
if (/\b(?:tsc|tsx)\b|build:(?:backend|frontend)|\bnpx\b|npm\s+(?:i|install|ci)\b/i.test(pkg.scripts.prepack || '')) fail('prepack must not require local development dependencies');
|
|
303
|
+
if (pkg.scripts.build !== 'npm run build:frontend && npm run build:backend && npm run clean:eos-runtime && npm run seal:eos-prebuilt-release') fail('build must clean and seal the prebuilt release after compilation');
|
|
271
304
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-role-access-selftest.cjs')) fail('role access selftest is not part of check:eos-stability');
|
|
272
305
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password flow selftest is not part of check:eos-stability');
|
|
273
306
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password management selftest is not part of check:eos-stability');
|
package/INSTALL_TEST_V7.10.1.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
package/RELEASE_NOTES_V7.10.1.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
(() => {
|
|
2
|
-
'use strict';
|
|
3
|
-
const RELEASE = '7.10.0';
|
|
4
|
-
const RELEASE_KEY = 'nexowatt.eos.loadedRelease';
|
|
5
|
-
const RELOAD_KEY = 'nexowatt.eos.reloadTarget';
|
|
6
|
-
const CHECK_MS = 15000;
|
|
7
|
-
let refreshing = false;
|
|
8
|
-
|
|
9
|
-
const normalize = value => String(value || '').trim().replace(/^v/i, '');
|
|
10
|
-
const validRelease = value => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(normalize(value));
|
|
11
|
-
const reloadUrl = version => {
|
|
12
|
-
const url = new URL(window.location.href);
|
|
13
|
-
url.searchParams.set('_eosRelease', normalize(version) || RELEASE);
|
|
14
|
-
url.searchParams.set('_eosReload', String(Date.now()));
|
|
15
|
-
return url.toString();
|
|
16
|
-
};
|
|
17
|
-
const purgeBrowserCaches = async () => {
|
|
18
|
-
const jobs = [];
|
|
19
|
-
try {
|
|
20
|
-
if ('caches' in window) {
|
|
21
|
-
jobs.push(caches.keys().then(keys => Promise.all(keys.map(key => caches.delete(key)))));
|
|
22
|
-
}
|
|
23
|
-
} catch (_) { /* Cache API is optional */ }
|
|
24
|
-
try {
|
|
25
|
-
if (navigator.serviceWorker?.getRegistrations) {
|
|
26
|
-
jobs.push(navigator.serviceWorker.getRegistrations().then(rows => Promise.all(rows.map(row => row.unregister()))));
|
|
27
|
-
}
|
|
28
|
-
} catch (_) { /* service workers may be unavailable */ }
|
|
29
|
-
await Promise.allSettled(jobs);
|
|
30
|
-
};
|
|
31
|
-
const reloadFor = async version => {
|
|
32
|
-
const target = normalize(version) || RELEASE;
|
|
33
|
-
if (refreshing || sessionStorage.getItem(RELOAD_KEY) === target) return;
|
|
34
|
-
refreshing = true;
|
|
35
|
-
sessionStorage.setItem(RELOAD_KEY, target);
|
|
36
|
-
document.documentElement.classList.add('eos-release-refreshing');
|
|
37
|
-
await purgeBrowserCaches();
|
|
38
|
-
window.location.replace(reloadUrl(target));
|
|
39
|
-
};
|
|
40
|
-
const checkServerVersion = async () => {
|
|
41
|
-
try {
|
|
42
|
-
const endpoint = new URL('./version', window.location.href);
|
|
43
|
-
endpoint.searchParams.set('_eosTs', String(Date.now()));
|
|
44
|
-
const response = await fetch(endpoint, {
|
|
45
|
-
cache: 'no-store',
|
|
46
|
-
credentials: 'same-origin',
|
|
47
|
-
headers: { 'Cache-Control': 'no-cache' },
|
|
48
|
-
});
|
|
49
|
-
if (!response.ok) return;
|
|
50
|
-
const serverVersion = normalize(await response.text());
|
|
51
|
-
if (validRelease(serverVersion) && serverVersion !== RELEASE) {
|
|
52
|
-
await reloadFor(serverVersion);
|
|
53
|
-
}
|
|
54
|
-
} catch (_) { /* adapter restart or temporary network interruption */ }
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
window.NEXOWATT_EOS_RELEASE = RELEASE;
|
|
58
|
-
const previous = normalize(localStorage.getItem(RELEASE_KEY));
|
|
59
|
-
localStorage.setItem(RELEASE_KEY, RELEASE);
|
|
60
|
-
const loadedFor = normalize(new URL(window.location.href).searchParams.get('_eosRelease'));
|
|
61
|
-
if (previous && previous !== RELEASE && loadedFor !== RELEASE) {
|
|
62
|
-
void reloadFor(RELEASE);
|
|
63
|
-
} else {
|
|
64
|
-
sessionStorage.removeItem(RELOAD_KEY);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
window.addEventListener('focus', () => void checkServerVersion());
|
|
68
|
-
document.addEventListener('visibilitychange', () => {
|
|
69
|
-
if (document.visibilityState === 'visible') void checkServerVersion();
|
|
70
|
-
});
|
|
71
|
-
window.setInterval(() => void checkServerVersion(), CHECK_MS);
|
|
72
|
-
window.NEXOWATT_EOS_RELEASE_WATCH = Object.freeze({
|
|
73
|
-
release: RELEASE,
|
|
74
|
-
check: checkServerVersion,
|
|
75
|
-
purge: purgeBrowserCaches,
|
|
76
|
-
});
|
|
77
|
-
})();
|