iobroker.script-restore 0.0.1 → 0.0.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/admin/tab_m.html CHANGED
@@ -244,6 +244,58 @@
244
244
  .dropdown-empty { padding: 12px; color: #6c757d; font-size: 0.875rem; text-align: center; }
245
245
  .dropdown-loading { padding: 12px; color: #6c757d; font-size: 0.875rem; text-align: center; }
246
246
 
247
+ /* Dark theme (ioBroker adds class "dark" to body) */
248
+ body.dark {
249
+ background: #1a1a2e;
250
+ color: #e0e0e0;
251
+ }
252
+ body.dark .toolbar {
253
+ background: #252537;
254
+ border-bottom-color: #3a3a5c;
255
+ }
256
+ body.dark .sidebar {
257
+ background: #252537;
258
+ border-right-color: #3a3a5c;
259
+ }
260
+ body.dark .sidebar-header { border-bottom-color: #3a3a5c; }
261
+ body.dark .sidebar-header input {
262
+ background: #1a1a2e;
263
+ border-color: #4a4a6a;
264
+ color: #e0e0e0;
265
+ }
266
+ body.dark .btn-icon {
267
+ background: #3a3a5c;
268
+ border-color: #4a4a6a;
269
+ color: #c0c0d0;
270
+ }
271
+ body.dark .btn-icon:hover { background: #4a4a70; }
272
+ body.dark .btn-outline {
273
+ color: #c0c0d0;
274
+ border-color: #4a4a6a;
275
+ background: #2e2e4a;
276
+ }
277
+ body.dark .btn-outline:hover { background: #3a3a5c; }
278
+ body.dark .tree-folder { color: #c0c0d0; }
279
+ body.dark .tree-folder:hover { background-color: #2e3a5c; }
280
+ body.dark .script-item { border-bottom-color: #2e2e4a; }
281
+ body.dark .script-item:hover { background-color: #2e3a5c; }
282
+ body.dark .script-item.active { background-color: #1e3a6e; }
283
+ body.dark .status-msg { color: #8888aa; }
284
+ body.dark .resizer { background-color: #3a3a5c; }
285
+ body.dark .dropdown-menu {
286
+ background: #252537;
287
+ border-color: #3a3a5c;
288
+ }
289
+ body.dark .dropdown-item { border-bottom-color: #3a3a5c; color: #e0e0e0; }
290
+ body.dark .dropdown-item:hover { background: #2e3a5c; }
291
+ body.dark .dropdown-empty,
292
+ body.dark .dropdown-loading { color: #8888aa; }
293
+ body.dark #loader {
294
+ background: rgba(26,26,46,0.95);
295
+ }
296
+ body.dark .progress-inner { background: rgba(26,26,46,0.95); }
297
+ body.dark #loaderText { color: #c0c0d0; }
298
+
247
299
  /* Responsive */
248
300
  @media (max-width: 768px) {
249
301
  .toolbar { padding: 6px 8px; }
@@ -316,6 +368,75 @@
316
368
  const instance = urlParams.get('instance') || '0';
317
369
  const adapterInst = 'script-restore.' + instance;
318
370
 
371
+ // === Dark Theme Detection ===
372
+ function applyTheme(isDark) {
373
+ document.body.classList.toggle('dark', !!isDark);
374
+ }
375
+
376
+ function detectTheme() {
377
+ // 1. URL parameter
378
+ const urlTheme = urlParams.get('theme') || urlParams.get('themeType') || urlParams.get('themeName');
379
+ if (urlTheme) { applyTheme(urlTheme === 'dark'); return; }
380
+
381
+ // 2. localStorage (ioBroker admin stores theme here)
382
+ try {
383
+ const stored = localStorage.getItem('App.theme') || localStorage.getItem('themeType') || localStorage.getItem('iobroker.ui.theme');
384
+ if (stored) { applyTheme(stored === 'dark'); return; }
385
+ } catch(e) {}
386
+
387
+ // 3. Parent frame body/html class or attribute
388
+ try {
389
+ const p = window.parent;
390
+ if (p && p.document) {
391
+ const pb = p.document.body;
392
+ const ph = p.document.documentElement;
393
+ if (pb.classList.contains('dark') || ph.classList.contains('dark') ||
394
+ ph.getAttribute('data-theme') === 'dark' ||
395
+ pb.getAttribute('data-theme') === 'dark') {
396
+ applyTheme(true); return;
397
+ }
398
+ // MUI dark mode: look for dark background color hint
399
+ const bg = window.getComputedStyle(pb).backgroundColor;
400
+ if (bg) {
401
+ const m = bg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/);
402
+ if (m && (+m[1] + +m[2] + +m[3]) < 200) { applyTheme(true); return; }
403
+ }
404
+ }
405
+ } catch(e) {}
406
+
407
+ applyTheme(false);
408
+ }
409
+
410
+ detectTheme();
411
+
412
+ // Re-detect on postMessage from parent (theme switch at runtime)
413
+ window.addEventListener('message', function(e) {
414
+ if (!e.data) return;
415
+ if (e.data.theme !== undefined) applyTheme(e.data.theme === 'dark');
416
+ if (e.data.themeType !== undefined) applyTheme(e.data.themeType === 'dark');
417
+ });
418
+
419
+ // Observe parent body for class/attribute/style changes (DOM-based theme switch)
420
+ try {
421
+ const pb = window.parent.document.body;
422
+ const ph = window.parent.document.documentElement;
423
+ const obs = new MutationObserver(() => detectTheme());
424
+ obs.observe(pb, { attributes: true, attributeFilter: ['class', 'data-theme', 'style'] });
425
+ obs.observe(ph, { attributes: true, attributeFilter: ['class', 'data-theme', 'style'] });
426
+ } catch(e) {}
427
+
428
+ // localStorage change (ioBroker admin v5+ stores theme there)
429
+ window.addEventListener('storage', function(e) {
430
+ if (e.key === 'App.theme' || e.key === 'themeType' || e.key === 'iobroker.ui.theme') {
431
+ detectTheme();
432
+ }
433
+ });
434
+
435
+ // Re-detect when tab becomes visible again (user switched tab/window)
436
+ document.addEventListener('visibilitychange', function() {
437
+ if (document.visibilityState === 'visible') detectTheme();
438
+ });
439
+
319
440
  let socket = null;
320
441
  let socketReady = false;
321
442
 
package/admin/words.js CHANGED
@@ -7,13 +7,40 @@ systemDictionary = {
7
7
  "script-restore adapter settings": {
8
8
  en: "Adapter settings for script-restore",
9
9
  de: "Adaptereinstellungen für script-restore",
10
+ ru: "Настройки адаптера script-restore",
11
+ pt: "Configurações do adaptador script-restore",
12
+ nl: "Adapterinstellingen voor script-restore",
13
+ fr: "Paramètres de l'adaptateur script-restore",
14
+ it: "Impostazioni adattatore script-restore",
15
+ es: "Configuración del adaptador script-restore",
16
+ pl: "Ustawienia adaptera script-restore",
17
+ uk: "Налаштування адаптера script-restore",
18
+ "zh-cn": "script-restore 适配器设置",
10
19
  },
11
20
  "backupPath": {
12
21
  en: "Backup path",
13
22
  de: "Backup-Pfad",
23
+ ru: "Путь к резервным копиям",
24
+ pt: "Caminho de backup",
25
+ nl: "Back-uppad",
26
+ fr: "Chemin de sauvegarde",
27
+ it: "Percorso di backup",
28
+ es: "Ruta de copia de seguridad",
29
+ pl: "Ścieżka kopii zapasowej",
30
+ uk: "Шлях до резервних копій",
31
+ "zh-cn": "备份路径",
14
32
  },
15
33
  "backupPathHint": {
16
34
  en: "Directory where ioBroker backup files are stored",
17
35
  de: "Verzeichnis, in dem ioBroker-Backup-Dateien gespeichert sind",
36
+ ru: "Каталог, в котором хранятся резервные файлы ioBroker",
37
+ pt: "Diretório onde os arquivos de backup do ioBroker são armazenados",
38
+ nl: "Map waar ioBroker-back-upbestanden worden opgeslagen",
39
+ fr: "Répertoire où sont stockés les fichiers de sauvegarde ioBroker",
40
+ it: "Directory in cui vengono archiviati i file di backup di ioBroker",
41
+ es: "Directorio donde se almacenan los archivos de copia de seguridad de ioBroker",
42
+ pl: "Katalog, w którym przechowywane są pliki kopii zapasowych ioBroker",
43
+ uk: "Каталог, де зберігаються файли резервних копій ioBroker",
44
+ "zh-cn": "存储 ioBroker 备份文件的目录",
18
45
  },
19
46
  };
package/build/main.js CHANGED
@@ -23,11 +23,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
  var utils = __toESM(require("@iobroker/adapter-core"));
25
25
  var fs = __toESM(require("fs/promises"));
26
- var path = __toESM(require("path"));
27
- var os = __toESM(require("os"));
28
- var import_child_process = require("child_process");
29
- var import_util = require("util");
30
- const execAsync = (0, import_util.promisify)(import_child_process.exec);
26
+ var path = __toESM(require("node:path"));
27
+ var os = __toESM(require("node:os"));
28
+ var import_node_child_process = require("node:child_process");
29
+ var import_node_util = require("node:util");
30
+ const execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
31
31
  class ScriptRestore extends utils.Adapter {
32
32
  constructor(options = {}) {
33
33
  super({
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["/*\n * ioBroker Script Restore Adapter\n * Restore ioBroker scripts from backup archives\n * Copyright (c) 2024 ipod86 <david@graef.email>\n * MIT License\n */\n\nimport type { Dirent } from \"fs\";\nimport * as utils from \"@iobroker/adapter-core\";\nimport * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport * as os from \"os\";\nimport { exec } from \"child_process\";\nimport { promisify } from \"util\";\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: string;\n}\n\ninterface AdapterConfig {\n\tbackupPath: string;\n}\n\nclass ScriptRestore extends utils.Adapter {\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: \"script-restore\",\n\t\t});\n\t\tthis.on(\"ready\", this.onReady.bind(this));\n\t\tthis.on(\"message\", this.onMessage.bind(this));\n\t\tthis.on(\"unload\", this.onUnload.bind(this));\n\t}\n\n\tprivate onReady(): void {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tthis.log.info(`Script Restore ready. Backup path: ${cfg.backupPath || \"/opt/iobroker/backups\"}`);\n\t}\n\n\tprivate onUnload(callback: () => void): void {\n\t\tcallback();\n\t}\n\n\tprivate async onMessage(obj: ioBroker.Message): Promise<void> {\n\t\tif (!obj.callback) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tswitch (obj.command) {\n\t\t\t\tcase \"listLocalFiles\":\n\t\t\t\t\tawait this.handleListLocalFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseLocalFile\":\n\t\t\t\t\tawait this.handleParseLocalFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseUploadedFile\":\n\t\t\t\t\tawait this.handleParseUploadedFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.log.error(`Error handling ${obj.command}: ${(e as Error).message}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tconst backupPath = cfg.backupPath || \"/opt/iobroker/backups\";\n\t\ttry {\n\t\t\tconst rawEntries = await fs.readdir(backupPath, { withFileTypes: true, encoding: \"utf8\" });\n\t\t\tconst entries = rawEntries as unknown as Dirent[];\n\t\t\tconst files = entries\n\t\t\t\t.filter(e => {\n\t\t\t\t\tconst n = String(e.name);\n\t\t\t\t\treturn (\n\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(e => String(e.name))\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: backupPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{ error: `Verzeichnis nicht lesbar: ${(e as Error).message}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleParseLocalFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tconst backupPath = cfg.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\n\t\t// Security: only allow simple filenames, no path traversal\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst filepath = path.join(backupPath, filename);\n\t\ttry {\n\t\t\tconst buf = await fs.readFile(filepath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseUploadedFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { name: string; data: string };\n\t\ttry {\n\t\t\tconst buf = Buffer.from(msg.data, \"base64\");\n\t\t\tconst scripts = await this.parseBuffer(buf, msg.name);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async parseBuffer(buf: Buffer, filename: string): Promise<ScriptEntry[]> {\n\t\tconst name = filename.toLowerCase();\n\t\tif (name.endsWith(\".tar.gz\") || name.endsWith(\".tgz\") || name.endsWith(\".tar\")) {\n\t\t\treturn this.parseTarArchive(buf, name.endsWith(\".tar\") && !name.endsWith(\".tar.gz\"));\n\t\t}\n\t\treturn this.parseJsonContent(buf.toString(\"utf8\"), filename);\n\t}\n\n\tprivate async parseTarArchive(buf: Buffer, isPlainTar: boolean): Promise<ScriptEntry[]> {\n\t\tconst tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"script-restore-\"));\n\t\tconst tmpFile = path.join(tmpDir, `archive.tar${isPlainTar ? \"\" : \".gz\"}`);\n\t\ttry {\n\t\t\tawait fs.writeFile(tmpFile, buf);\n\n\t\t\tconst extractFlag = isPlainTar ? \"-xf\" : \"-xzf\";\n\t\t\t// Extract only matching files to avoid extracting entire large archives\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" --wildcards` +\n\t\t\t\t\t\t` \"*/objects.jsonl\" \"*/objects.json\" \"*/scripts.json\" \"*/script.json\"` +\n\t\t\t\t\t\t` 2>/dev/null`,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// Try without wildcards filter (some tar versions behave differently)\n\t\t\t\tawait execAsync(`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" 2>/dev/null`).catch(() => {});\n\t\t\t}\n\n\t\t\tconst targets = [\"objects.jsonl\", \"objects.json\", \"scripts.json\", \"script.json\"];\n\t\t\tconst found = await this.findFile(tmpDir, targets);\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json, script.json)\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(found, \"utf8\");\n\t\t\treturn this.parseJsonContent(content, path.basename(found));\n\t\t} finally {\n\t\t\tawait fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async findFile(dir: string, names: string[]): Promise<string | null> {\n\t\tconst walk = async (d: string): Promise<string | null> => {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = (await fs.readdir(d, {\n\t\t\t\t\twithFileTypes: true,\n\t\t\t\t\tencoding: \"utf8\",\n\t\t\t\t})) as unknown as Dirent[];\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (const e of entries) {\n\t\t\t\tconst p = path.join(d, String(e.name));\n\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\tconst found = await walk(p);\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\treturn found;\n\t\t\t\t\t}\n\t\t\t\t} else if (names.includes(String(e.name))) {\n\t\t\t\t\treturn p;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\treturn walk(dir);\n\t}\n\n\tprivate parseJsonContent(content: string, filename: string): ScriptEntry[] {\n\t\tconst scripts: ScriptEntry[] = [];\n\t\tconst trimmed = content.trimStart();\n\n\t\t// Detect JSONL format: file ends with .jsonl or starts with single JSON object per line\n\t\tconst isJsonl =\n\t\t\tfilename.endsWith(\".jsonl\") ||\n\t\t\t(trimmed.startsWith(\"{\") && !trimmed.startsWith('{\\n \"') && trimmed.includes(\"\\n{\"));\n\n\t\tif (isJsonl) {\n\t\t\tfor (const line of content.split(\"\\n\")) {\n\t\t\t\tconst l = line.trim();\n\t\t\t\tif (!l) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst item = JSON.parse(l) as Record<string, unknown>;\n\t\t\t\t\tthis.processItem(\n\t\t\t\t\t\t(item._id || item.id) as string,\n\t\t\t\t\t\t(item.value || item.doc || item) as Record<string, unknown>,\n\t\t\t\t\t\tscripts,\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// skip invalid lines\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = JSON.parse(content) as Record<string, unknown>;\n\t\t\tfor (const [k, v] of Object.entries(data)) {\n\t\t\t\tthis.processItem(k, v as Record<string, unknown>, scripts);\n\t\t\t}\n\t\t}\n\n\t\treturn scripts.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n\t}\n\n\tprivate processItem(key: string, val: unknown, scripts: ScriptEntry[]): void {\n\t\tif (!key || typeof val !== \"object\" || val === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst v = val as Record<string, unknown>;\n\n\t\tif ([\"channel\", \"device\", \"folder\", \"meta\"].includes(v.type as string)) {\n\t\t\treturn;\n\t\t}\n\t\tif (v.type !== \"script\" && !key.startsWith(\"script.js.\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = v.common as Record<string, unknown> | undefined;\n\t\tif (!c || (c.engineType === undefined && c.source === undefined)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawEngineType = typeof c.engineType === \"string\" ? c.engineType : \"JS\";\n\t\tconst engineType = rawEngineType.toLowerCase();\n\t\tlet stype: string;\n\t\tif (engineType.includes(\"ts\") || engineType.includes(\"typescript\")) {\n\t\t\tstype = \"TypeScript\";\n\t\t} else if (engineType.includes(\"blockly\")) {\n\t\t\tstype = \"Blockly\";\n\t\t} else if (engineType.includes(\"rules\")) {\n\t\t\tstype = \"Rules\";\n\t\t} else {\n\t\t\tstype = \"JS\";\n\t\t}\n\n\t\tlet name: string;\n\t\tconst nameObj = c.name;\n\t\tif (typeof nameObj === \"object\" && nameObj !== null) {\n\t\t\tconst n = nameObj as Record<string, string>;\n\t\t\tname = n.de || n.en || Object.values(n)[0] || key.split(\".\").pop() || key;\n\t\t} else {\n\t\t\tname = typeof nameObj === \"string\" && nameObj ? nameObj : (key.split(\".\").pop() ?? key);\n\t\t}\n\n\t\tconst scriptPath = key.startsWith(\"script.js.\") ? key.slice(10) : key;\n\n\t\tscripts.push({\n\t\t\tname,\n\t\t\tpath: scriptPath,\n\t\t\ttype: stype,\n\t\t\tsource: typeof c.source === \"string\" ? c.source : \"\",\n\t\t});\n\t}\n}\n\nif (require.main !== module) {\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ScriptRestore(options);\n} else {\n\t(() => new ScriptRestore())();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n * ioBroker Script Restore Adapter\n * Restore ioBroker scripts from backup archives\n * Copyright (c) 2024 ipod86 <david@graef.email>\n * MIT License\n */\n\nimport type { Dirent } from \"node:fs\";\nimport * as utils from \"@iobroker/adapter-core\";\nimport * as fs from \"fs/promises\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: string;\n}\n\ninterface AdapterConfig {\n\tbackupPath: string;\n}\n\nclass ScriptRestore extends utils.Adapter {\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: \"script-restore\",\n\t\t});\n\t\tthis.on(\"ready\", this.onReady.bind(this));\n\t\tthis.on(\"message\", this.onMessage.bind(this));\n\t\tthis.on(\"unload\", this.onUnload.bind(this));\n\t}\n\n\tprivate onReady(): void {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tthis.log.info(`Script Restore ready. Backup path: ${cfg.backupPath || \"/opt/iobroker/backups\"}`);\n\t}\n\n\tprivate onUnload(callback: () => void): void {\n\t\tcallback();\n\t}\n\n\tprivate async onMessage(obj: ioBroker.Message): Promise<void> {\n\t\tif (!obj.callback) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tswitch (obj.command) {\n\t\t\t\tcase \"listLocalFiles\":\n\t\t\t\t\tawait this.handleListLocalFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseLocalFile\":\n\t\t\t\t\tawait this.handleParseLocalFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseUploadedFile\":\n\t\t\t\t\tawait this.handleParseUploadedFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.log.error(`Error handling ${obj.command}: ${(e as Error).message}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tconst backupPath = cfg.backupPath || \"/opt/iobroker/backups\";\n\t\ttry {\n\t\t\tconst rawEntries = await fs.readdir(backupPath, { withFileTypes: true, encoding: \"utf8\" });\n\t\t\tconst entries = rawEntries as unknown as Dirent[];\n\t\t\tconst files = entries\n\t\t\t\t.filter(e => {\n\t\t\t\t\tconst n = String(e.name);\n\t\t\t\t\treturn (\n\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(e => String(e.name))\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: backupPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{ error: `Verzeichnis nicht lesbar: ${(e as Error).message}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleParseLocalFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst cfg = this.config as unknown as AdapterConfig;\n\t\tconst backupPath = cfg.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\n\t\t// Security: only allow simple filenames, no path traversal\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst filepath = path.join(backupPath, filename);\n\t\ttry {\n\t\t\tconst buf = await fs.readFile(filepath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseUploadedFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { name: string; data: string };\n\t\ttry {\n\t\t\tconst buf = Buffer.from(msg.data, \"base64\");\n\t\t\tconst scripts = await this.parseBuffer(buf, msg.name);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async parseBuffer(buf: Buffer, filename: string): Promise<ScriptEntry[]> {\n\t\tconst name = filename.toLowerCase();\n\t\tif (name.endsWith(\".tar.gz\") || name.endsWith(\".tgz\") || name.endsWith(\".tar\")) {\n\t\t\treturn this.parseTarArchive(buf, name.endsWith(\".tar\") && !name.endsWith(\".tar.gz\"));\n\t\t}\n\t\treturn this.parseJsonContent(buf.toString(\"utf8\"), filename);\n\t}\n\n\tprivate async parseTarArchive(buf: Buffer, isPlainTar: boolean): Promise<ScriptEntry[]> {\n\t\tconst tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"script-restore-\"));\n\t\tconst tmpFile = path.join(tmpDir, `archive.tar${isPlainTar ? \"\" : \".gz\"}`);\n\t\ttry {\n\t\t\tawait fs.writeFile(tmpFile, buf);\n\n\t\t\tconst extractFlag = isPlainTar ? \"-xf\" : \"-xzf\";\n\t\t\t// Extract only matching files to avoid extracting entire large archives\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" --wildcards` +\n\t\t\t\t\t\t` \"*/objects.jsonl\" \"*/objects.json\" \"*/scripts.json\" \"*/script.json\"` +\n\t\t\t\t\t\t` 2>/dev/null`,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// Try without wildcards filter (some tar versions behave differently)\n\t\t\t\tawait execAsync(`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" 2>/dev/null`).catch(() => {});\n\t\t\t}\n\n\t\t\tconst targets = [\"objects.jsonl\", \"objects.json\", \"scripts.json\", \"script.json\"];\n\t\t\tconst found = await this.findFile(tmpDir, targets);\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json, script.json)\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(found, \"utf8\");\n\t\t\treturn this.parseJsonContent(content, path.basename(found));\n\t\t} finally {\n\t\t\tawait fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async findFile(dir: string, names: string[]): Promise<string | null> {\n\t\tconst walk = async (d: string): Promise<string | null> => {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = (await fs.readdir(d, {\n\t\t\t\t\twithFileTypes: true,\n\t\t\t\t\tencoding: \"utf8\",\n\t\t\t\t})) as unknown as Dirent[];\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (const e of entries) {\n\t\t\t\tconst p = path.join(d, String(e.name));\n\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\tconst found = await walk(p);\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\treturn found;\n\t\t\t\t\t}\n\t\t\t\t} else if (names.includes(String(e.name))) {\n\t\t\t\t\treturn p;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\treturn walk(dir);\n\t}\n\n\tprivate parseJsonContent(content: string, filename: string): ScriptEntry[] {\n\t\tconst scripts: ScriptEntry[] = [];\n\t\tconst trimmed = content.trimStart();\n\n\t\t// Detect JSONL format: file ends with .jsonl or starts with single JSON object per line\n\t\tconst isJsonl =\n\t\t\tfilename.endsWith(\".jsonl\") ||\n\t\t\t(trimmed.startsWith(\"{\") && !trimmed.startsWith('{\\n \"') && trimmed.includes(\"\\n{\"));\n\n\t\tif (isJsonl) {\n\t\t\tfor (const line of content.split(\"\\n\")) {\n\t\t\t\tconst l = line.trim();\n\t\t\t\tif (!l) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst item = JSON.parse(l) as Record<string, unknown>;\n\t\t\t\t\tthis.processItem(\n\t\t\t\t\t\t(item._id || item.id) as string,\n\t\t\t\t\t\t(item.value || item.doc || item) as Record<string, unknown>,\n\t\t\t\t\t\tscripts,\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// skip invalid lines\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = JSON.parse(content) as Record<string, unknown>;\n\t\t\tfor (const [k, v] of Object.entries(data)) {\n\t\t\t\tthis.processItem(k, v as Record<string, unknown>, scripts);\n\t\t\t}\n\t\t}\n\n\t\treturn scripts.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n\t}\n\n\tprivate processItem(key: string, val: unknown, scripts: ScriptEntry[]): void {\n\t\tif (!key || typeof val !== \"object\" || val === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst v = val as Record<string, unknown>;\n\n\t\tif ([\"channel\", \"device\", \"folder\", \"meta\"].includes(v.type as string)) {\n\t\t\treturn;\n\t\t}\n\t\tif (v.type !== \"script\" && !key.startsWith(\"script.js.\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = v.common as Record<string, unknown> | undefined;\n\t\tif (!c || (c.engineType === undefined && c.source === undefined)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawEngineType = typeof c.engineType === \"string\" ? c.engineType : \"JS\";\n\t\tconst engineType = rawEngineType.toLowerCase();\n\t\tlet stype: string;\n\t\tif (engineType.includes(\"ts\") || engineType.includes(\"typescript\")) {\n\t\t\tstype = \"TypeScript\";\n\t\t} else if (engineType.includes(\"blockly\")) {\n\t\t\tstype = \"Blockly\";\n\t\t} else if (engineType.includes(\"rules\")) {\n\t\t\tstype = \"Rules\";\n\t\t} else {\n\t\t\tstype = \"JS\";\n\t\t}\n\n\t\tlet name: string;\n\t\tconst nameObj = c.name;\n\t\tif (typeof nameObj === \"object\" && nameObj !== null) {\n\t\t\tconst n = nameObj as Record<string, string>;\n\t\t\tname = n.de || n.en || Object.values(n)[0] || key.split(\".\").pop() || key;\n\t\t} else {\n\t\t\tname = typeof nameObj === \"string\" && nameObj ? nameObj : (key.split(\".\").pop() ?? key);\n\t\t}\n\n\t\tconst scriptPath = key.startsWith(\"script.js.\") ? key.slice(10) : key;\n\n\t\tscripts.push({\n\t\t\tname,\n\t\t\tpath: scriptPath,\n\t\t\ttype: stype,\n\t\t\tsource: typeof c.source === \"string\" ? c.source : \"\",\n\t\t});\n\t}\n}\n\nif (require.main !== module) {\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ScriptRestore(options);\n} else {\n\t(() => new ScriptRestore())();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/io-package.json CHANGED
@@ -1,95 +1,121 @@
1
1
  {
2
- "common": {
3
- "name": "script-restore",
4
- "version": "0.0.1",
5
- "news": {
6
- "0.0.1": {
7
- "en": "Initial release",
8
- "de": "Erstveröffentlichung",
9
- "ru": "Начальная версия",
10
- "pt": "Versão inicial",
11
- "nl": "Eerste versie",
12
- "fr": "Version initiale",
13
- "it": "Versione iniziale",
14
- "es": "Versión inicial",
15
- "pl": "Pierwsze wydanie",
16
- "uk": "Початкова версія",
17
- "zh-cn": "首次出版"
18
- }
19
- },
20
- "titleLang": {
21
- "en": "Script Restore",
22
- "de": "Script Restore",
23
- "ru": "Script Restore",
24
- "pt": "Script Restore",
25
- "nl": "Script Restore",
26
- "fr": "Script Restore",
27
- "it": "Script Restore",
28
- "es": "Script Restore",
29
- "pl": "Script Restore",
30
- "uk": "Script Restore",
31
- "zh-cn": "Script Restore"
32
- },
33
- "desc": {
34
- "en": "Restore ioBroker scripts from backup archives",
35
- "de": "ioBroker-Skripte aus Backup-Archiven wiederherstellen",
36
- "ru": "Восстановление скриптов ioBroker из резервных архивов",
37
- "pt": "Restaurar scripts ioBroker de arquivos de backup",
38
- "nl": "Herstel ioBroker-scripts vanuit back-uparchieven",
39
- "fr": "Restaurer les scripts ioBroker à partir des archives de sauvegarde",
40
- "it": "Ripristina gli script ioBroker dagli archivi di backup",
41
- "es": "Restaurar scripts de ioBroker desde archivos de respaldo",
42
- "pl": "Przywróć skrypty ioBroker z archiwów kopii zapasowych",
43
- "uk": "Відновлення скриптів ioBroker з резервних архівів",
44
- "zh-cn": "从备份存档中恢复 ioBroker 脚本"
45
- },
46
- "authors": ["ipod86 <david@graef.email>"],
47
- "keywords": [
48
- "backup",
49
- "restore",
50
- "scripts",
51
- "javascript",
52
- "iobroker"
53
- ],
54
- "licenseInformation": {
55
- "type": "free",
56
- "license": "MIT"
57
- },
58
- "platform": "Javascript/Node.js",
59
- "icon": "script-restore.png",
60
- "enabled": true,
61
- "extIcon": "https://raw.githubusercontent.com/ipod86/ioBroker.script-restore/main/admin/script-restore.png",
62
- "readme": "https://github.com/ipod86/ioBroker.script-restore/blob/main/README.md",
63
- "loglevel": "info",
64
- "tier": 3,
65
- "mode": "daemon",
66
- "type": "utility",
67
- "compact": true,
68
- "messagebox": true,
69
- "connectionType": "local",
70
- "dataSource": "push",
71
- "adminUI": {
72
- "config": "materialize",
73
- "tab": "materialize"
74
- },
75
- "adminTab": {
76
- "singleton": true,
77
- "name": "Script Restore"
78
- },
79
- "dependencies": [
80
- {
81
- "js-controller": ">=6.0.11"
82
- }
83
- ],
84
- "globalDependencies": [
85
- {
86
- "admin": ">=7.0.23"
87
- }
88
- ]
89
- },
90
- "native": {
91
- "backupPath": "/opt/iobroker/backups"
92
- },
93
- "objects": [],
94
- "instanceObjects": []
2
+ "common": {
3
+ "name": "script-restore",
4
+ "version": "0.0.3",
5
+ "news": {
6
+ "0.0.2": {
7
+ "en": "update README, fix SVG icon dimensions, add full language translations",
8
+ "de": "rEADME aktualisieren, SVG-Icondimensionen beheben, vollständige Übersetzungen hinzufügen",
9
+ "ru": "обновление README, исправление размеров значков SVG, добавление полных языковых переводов",
10
+ "pt": "atualizar README, corrigir dimensões do ícone SVG, adicionar traduções completas do idioma",
11
+ "nl": "update README, fix SVG pictogram afmetingen, voeg volledige taal vertalingen",
12
+ "fr": "mettre à jour README, corriger les dimensions des icônes SVG, ajouter des traductions en langue complète",
13
+ "it": "aggiornare README, correggere le dimensioni dell'icona SVG, aggiungere traduzioni in lingua completa",
14
+ "es": "actualizar README, fijar dimensiones de icono SVG, añadir traducciones completas del idioma",
15
+ "pl": "aktualizacja README, poprawianie wymiarów ikon SVG, dodawanie pełnych tłumaczeń językowych",
16
+ "uk": "оновлення README, зафіксувати розміри значків SVG, додати повномовні переклади",
17
+ "zh-cn": "更新 README, 修正 SVG 图标尺寸, 添加完整的语言翻译"
18
+ },
19
+ "0.0.1": {
20
+ "en": "Initial release",
21
+ "de": "Erstveröffentlichung",
22
+ "ru": "Начальная версия",
23
+ "pt": "Versão inicial",
24
+ "nl": "Eerste versie",
25
+ "fr": "Version initiale",
26
+ "it": "Versione iniziale",
27
+ "es": "Versión inicial",
28
+ "pl": "Pierwsze wydanie",
29
+ "uk": "Початкова версія",
30
+ "zh-cn": "首次出版"
31
+ }
32
+ },
33
+ "titleLang": {
34
+ "en": "Script Restore",
35
+ "de": "Script Restore",
36
+ "ru": "Script Restore",
37
+ "pt": "Script Restore",
38
+ "nl": "Script Restore",
39
+ "fr": "Script Restore",
40
+ "it": "Script Restore",
41
+ "es": "Script Restore",
42
+ "pl": "Script Restore",
43
+ "uk": "Script Restore",
44
+ "zh-cn": "Script Restore"
45
+ },
46
+ "desc": {
47
+ "en": "Restore ioBroker scripts from backup archives",
48
+ "de": "ioBroker-Skripte aus Backup-Archiven wiederherstellen",
49
+ "ru": "Восстановление скриптов ioBroker из резервных архивов",
50
+ "pt": "Restaurar scripts ioBroker de arquivos de backup",
51
+ "nl": "Herstel ioBroker-scripts vanuit back-uparchieven",
52
+ "fr": "Restaurer les scripts ioBroker à partir des archives de sauvegarde",
53
+ "it": "Ripristina gli script ioBroker dagli archivi di backup",
54
+ "es": "Restaurar scripts de ioBroker desde archivos de respaldo",
55
+ "pl": "Przywróć skrypty ioBroker z archiwów kopii zapasowych",
56
+ "uk": "Відновлення скриптів ioBroker з резервних архівів",
57
+ "zh-cn": "从备份存档中恢复 ioBroker 脚本"
58
+ },
59
+ "authors": [
60
+ "ipod86 <david@graef.email>"
61
+ ],
62
+ "keywords": [
63
+ "backup",
64
+ "restore",
65
+ "scripts",
66
+ "javascript"
67
+ ],
68
+ "licenseInformation": {
69
+ "type": "free",
70
+ "license": "MIT"
71
+ },
72
+ "platform": "Javascript/Node.js",
73
+ "icon": "script-restore.svg",
74
+ "enabled": true,
75
+ "extIcon": "https://raw.githubusercontent.com/ipod86/ioBroker.script-restore/main/admin/script-restore.svg",
76
+ "readme": "https://github.com/ipod86/ioBroker.script-restore/blob/main/README.md",
77
+ "loglevel": "info",
78
+ "tier": 3,
79
+ "mode": "daemon",
80
+ "type": "utility",
81
+ "compact": true,
82
+ "messagebox": true,
83
+ "connectionType": "local",
84
+ "dataSource": "push",
85
+ "adminUI": {
86
+ "config": "materialize",
87
+ "tab": "materialize"
88
+ },
89
+ "adminTab": {
90
+ "singleton": true,
91
+ "name": {
92
+ "en": "Script Restore",
93
+ "de": "Script Restore",
94
+ "ru": "Script Restore",
95
+ "pt": "Script Restore",
96
+ "nl": "Script Restore",
97
+ "fr": "Script Restore",
98
+ "it": "Script Restore",
99
+ "es": "Script Restore",
100
+ "pl": "Script Restore",
101
+ "uk": "Script Restore",
102
+ "zh-cn": "Script Restore"
103
+ }
104
+ },
105
+ "dependencies": [
106
+ {
107
+ "js-controller": ">=6.0.11"
108
+ }
109
+ ],
110
+ "globalDependencies": [
111
+ {
112
+ "admin": ">=7.6.20"
113
+ }
114
+ ]
115
+ },
116
+ "native": {
117
+ "backupPath": "/opt/iobroker/backups"
118
+ },
119
+ "objects": [],
120
+ "instanceObjects": []
95
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.script-restore",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Restore ioBroker scripts from backup archives",
5
5
  "author": {
6
6
  "name": "ipod86",
@@ -10,6 +10,7 @@
10
10
  "homepage": "https://github.com/ipod86/ioBroker.script-restore",
11
11
  "license": "MIT",
12
12
  "keywords": [
13
+ "ioBroker",
13
14
  "backup",
14
15
  "restore",
15
16
  "scripts",
@@ -21,7 +22,7 @@
21
22
  "url": "https://github.com/ipod86/ioBroker.script-restore.git"
22
23
  },
23
24
  "engines": {
24
- "node": ">= 18"
25
+ "node": ">= 20"
25
26
  },
26
27
  "dependencies": {
27
28
  "@iobroker/adapter-core": "^3.3.2"
@@ -36,7 +37,7 @@
36
37
  "@iobroker/testing": "^5.2.2",
37
38
  "@tsconfig/node18": "^18.2.6",
38
39
  "@types/iobroker": "npm:@iobroker/types@^7.1.0",
39
- "@types/node": "^18.19.130",
40
+ "@types/node": "^25.5.2",
40
41
  "rimraf": "^6.1.3",
41
42
  "source-map-support": "^0.5.21",
42
43
  "ts-node": "^10.9.2",
@@ -71,4 +72,4 @@
71
72
  "url": "https://github.com/ipod86/ioBroker.script-restore/issues"
72
73
  },
73
74
  "readmeFilename": "README.md"
74
- }
75
+ }
Binary file