iobroker.script-restore 0.0.1
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/LICENSE +21 -0
- package/README.md +159 -0
- package/admin/index_m.html +74 -0
- package/admin/script-restore.png +0 -0
- package/admin/style.css +32 -0
- package/admin/tab_m.html +898 -0
- package/admin/words.js +19 -0
- package/build/main.js +254 -0
- package/build/main.js.map +7 -0
- package/io-package.json +95 -0
- package/package.json +74 -0
package/admin/words.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint no-unused-vars: off */
|
|
2
|
+
/* eslint no-global-assign: off */
|
|
3
|
+
/* global systemDictionary */
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
systemDictionary = {
|
|
7
|
+
"script-restore adapter settings": {
|
|
8
|
+
en: "Adapter settings for script-restore",
|
|
9
|
+
de: "Adaptereinstellungen für script-restore",
|
|
10
|
+
},
|
|
11
|
+
"backupPath": {
|
|
12
|
+
en: "Backup path",
|
|
13
|
+
de: "Backup-Pfad",
|
|
14
|
+
},
|
|
15
|
+
"backupPathHint": {
|
|
16
|
+
en: "Directory where ioBroker backup files are stored",
|
|
17
|
+
de: "Verzeichnis, in dem ioBroker-Backup-Dateien gespeichert sind",
|
|
18
|
+
},
|
|
19
|
+
};
|
package/build/main.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var utils = __toESM(require("@iobroker/adapter-core"));
|
|
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);
|
|
31
|
+
class ScriptRestore extends utils.Adapter {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
super({
|
|
34
|
+
...options,
|
|
35
|
+
name: "script-restore"
|
|
36
|
+
});
|
|
37
|
+
this.on("ready", this.onReady.bind(this));
|
|
38
|
+
this.on("message", this.onMessage.bind(this));
|
|
39
|
+
this.on("unload", this.onUnload.bind(this));
|
|
40
|
+
}
|
|
41
|
+
onReady() {
|
|
42
|
+
const cfg = this.config;
|
|
43
|
+
this.log.info(`Script Restore ready. Backup path: ${cfg.backupPath || "/opt/iobroker/backups"}`);
|
|
44
|
+
}
|
|
45
|
+
onUnload(callback) {
|
|
46
|
+
callback();
|
|
47
|
+
}
|
|
48
|
+
async onMessage(obj) {
|
|
49
|
+
if (!obj.callback) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
switch (obj.command) {
|
|
54
|
+
case "listLocalFiles":
|
|
55
|
+
await this.handleListLocalFiles(obj);
|
|
56
|
+
break;
|
|
57
|
+
case "parseLocalFile":
|
|
58
|
+
await this.handleParseLocalFile(obj);
|
|
59
|
+
break;
|
|
60
|
+
case "parseUploadedFile":
|
|
61
|
+
await this.handleParseUploadedFile(obj);
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
this.sendTo(obj.from, obj.command, { error: "Unknown command" }, obj.callback);
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
this.log.error(`Error handling ${obj.command}: ${e.message}`);
|
|
68
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async handleListLocalFiles(obj) {
|
|
72
|
+
const cfg = this.config;
|
|
73
|
+
const backupPath = cfg.backupPath || "/opt/iobroker/backups";
|
|
74
|
+
try {
|
|
75
|
+
const rawEntries = await fs.readdir(backupPath, { withFileTypes: true, encoding: "utf8" });
|
|
76
|
+
const entries = rawEntries;
|
|
77
|
+
const files = entries.filter((e) => {
|
|
78
|
+
const n = String(e.name);
|
|
79
|
+
return e.isFile() && (n.startsWith("iobroker") || n.startsWith("javascript")) && (n.endsWith(".tar.gz") || n.endsWith(".tar") || n.endsWith(".json") || n.endsWith(".jsonl"));
|
|
80
|
+
}).map((e) => String(e.name)).sort().reverse();
|
|
81
|
+
this.sendTo(obj.from, obj.command, { files, path: backupPath }, obj.callback);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
this.sendTo(
|
|
84
|
+
obj.from,
|
|
85
|
+
obj.command,
|
|
86
|
+
{ error: `Verzeichnis nicht lesbar: ${e.message}` },
|
|
87
|
+
obj.callback
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async handleParseLocalFile(obj) {
|
|
92
|
+
const cfg = this.config;
|
|
93
|
+
const backupPath = cfg.backupPath || "/opt/iobroker/backups";
|
|
94
|
+
const msg = obj.message;
|
|
95
|
+
const filename = path.basename(msg.filename);
|
|
96
|
+
const filepath = path.join(backupPath, filename);
|
|
97
|
+
try {
|
|
98
|
+
const buf = await fs.readFile(filepath);
|
|
99
|
+
const scripts = await this.parseBuffer(buf, filename);
|
|
100
|
+
this.sendTo(obj.from, obj.command, { scripts }, obj.callback);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async handleParseUploadedFile(obj) {
|
|
106
|
+
const msg = obj.message;
|
|
107
|
+
try {
|
|
108
|
+
const buf = Buffer.from(msg.data, "base64");
|
|
109
|
+
const scripts = await this.parseBuffer(buf, msg.name);
|
|
110
|
+
this.sendTo(obj.from, obj.command, { scripts }, obj.callback);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async parseBuffer(buf, filename) {
|
|
116
|
+
const name = filename.toLowerCase();
|
|
117
|
+
if (name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".tar")) {
|
|
118
|
+
return this.parseTarArchive(buf, name.endsWith(".tar") && !name.endsWith(".tar.gz"));
|
|
119
|
+
}
|
|
120
|
+
return this.parseJsonContent(buf.toString("utf8"), filename);
|
|
121
|
+
}
|
|
122
|
+
async parseTarArchive(buf, isPlainTar) {
|
|
123
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "script-restore-"));
|
|
124
|
+
const tmpFile = path.join(tmpDir, `archive.tar${isPlainTar ? "" : ".gz"}`);
|
|
125
|
+
try {
|
|
126
|
+
await fs.writeFile(tmpFile, buf);
|
|
127
|
+
const extractFlag = isPlainTar ? "-xf" : "-xzf";
|
|
128
|
+
try {
|
|
129
|
+
await execAsync(
|
|
130
|
+
`tar ${extractFlag} "${tmpFile}" -C "${tmpDir}" --wildcards "*/objects.jsonl" "*/objects.json" "*/scripts.json" "*/script.json" 2>/dev/null`
|
|
131
|
+
);
|
|
132
|
+
} catch {
|
|
133
|
+
await execAsync(`tar ${extractFlag} "${tmpFile}" -C "${tmpDir}" 2>/dev/null`).catch(() => {
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const targets = ["objects.jsonl", "objects.json", "scripts.json", "script.json"];
|
|
137
|
+
const found = await this.findFile(tmpDir, targets);
|
|
138
|
+
if (!found) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
"Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json, script.json)"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const content = await fs.readFile(found, "utf8");
|
|
144
|
+
return this.parseJsonContent(content, path.basename(found));
|
|
145
|
+
} finally {
|
|
146
|
+
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async findFile(dir, names) {
|
|
151
|
+
const walk = async (d) => {
|
|
152
|
+
let entries;
|
|
153
|
+
try {
|
|
154
|
+
entries = await fs.readdir(d, {
|
|
155
|
+
withFileTypes: true,
|
|
156
|
+
encoding: "utf8"
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
for (const e of entries) {
|
|
162
|
+
const p = path.join(d, String(e.name));
|
|
163
|
+
if (e.isDirectory()) {
|
|
164
|
+
const found = await walk(p);
|
|
165
|
+
if (found) {
|
|
166
|
+
return found;
|
|
167
|
+
}
|
|
168
|
+
} else if (names.includes(String(e.name))) {
|
|
169
|
+
return p;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
};
|
|
174
|
+
return walk(dir);
|
|
175
|
+
}
|
|
176
|
+
parseJsonContent(content, filename) {
|
|
177
|
+
const scripts = [];
|
|
178
|
+
const trimmed = content.trimStart();
|
|
179
|
+
const isJsonl = filename.endsWith(".jsonl") || trimmed.startsWith("{") && !trimmed.startsWith('{\n "') && trimmed.includes("\n{");
|
|
180
|
+
if (isJsonl) {
|
|
181
|
+
for (const line of content.split("\n")) {
|
|
182
|
+
const l = line.trim();
|
|
183
|
+
if (!l) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const item = JSON.parse(l);
|
|
188
|
+
this.processItem(
|
|
189
|
+
item._id || item.id,
|
|
190
|
+
item.value || item.doc || item,
|
|
191
|
+
scripts
|
|
192
|
+
);
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
const data = JSON.parse(content);
|
|
198
|
+
for (const [k, v] of Object.entries(data)) {
|
|
199
|
+
this.processItem(k, v, scripts);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return scripts.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
203
|
+
}
|
|
204
|
+
processItem(key, val, scripts) {
|
|
205
|
+
var _a;
|
|
206
|
+
if (!key || typeof val !== "object" || val === null) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const v = val;
|
|
210
|
+
if (["channel", "device", "folder", "meta"].includes(v.type)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (v.type !== "script" && !key.startsWith("script.js.")) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const c = v.common;
|
|
217
|
+
if (!c || c.engineType === void 0 && c.source === void 0) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const rawEngineType = typeof c.engineType === "string" ? c.engineType : "JS";
|
|
221
|
+
const engineType = rawEngineType.toLowerCase();
|
|
222
|
+
let stype;
|
|
223
|
+
if (engineType.includes("ts") || engineType.includes("typescript")) {
|
|
224
|
+
stype = "TypeScript";
|
|
225
|
+
} else if (engineType.includes("blockly")) {
|
|
226
|
+
stype = "Blockly";
|
|
227
|
+
} else if (engineType.includes("rules")) {
|
|
228
|
+
stype = "Rules";
|
|
229
|
+
} else {
|
|
230
|
+
stype = "JS";
|
|
231
|
+
}
|
|
232
|
+
let name;
|
|
233
|
+
const nameObj = c.name;
|
|
234
|
+
if (typeof nameObj === "object" && nameObj !== null) {
|
|
235
|
+
const n = nameObj;
|
|
236
|
+
name = n.de || n.en || Object.values(n)[0] || key.split(".").pop() || key;
|
|
237
|
+
} else {
|
|
238
|
+
name = typeof nameObj === "string" && nameObj ? nameObj : (_a = key.split(".").pop()) != null ? _a : key;
|
|
239
|
+
}
|
|
240
|
+
const scriptPath = key.startsWith("script.js.") ? key.slice(10) : key;
|
|
241
|
+
scripts.push({
|
|
242
|
+
name,
|
|
243
|
+
path: scriptPath,
|
|
244
|
+
type: stype,
|
|
245
|
+
source: typeof c.source === "string" ? c.source : ""
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (require.main !== module) {
|
|
250
|
+
module.exports = (options) => new ScriptRestore(options);
|
|
251
|
+
} else {
|
|
252
|
+
(() => new ScriptRestore())();
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/io-package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
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": []
|
|
95
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iobroker.script-restore",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Restore ioBroker scripts from backup archives",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ipod86",
|
|
7
|
+
"email": "david@graef.email"
|
|
8
|
+
},
|
|
9
|
+
"contributors": [],
|
|
10
|
+
"homepage": "https://github.com/ipod86/ioBroker.script-restore",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"backup",
|
|
14
|
+
"restore",
|
|
15
|
+
"scripts",
|
|
16
|
+
"javascript",
|
|
17
|
+
"iobroker"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/ipod86/ioBroker.script-restore.git"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">= 18"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@iobroker/adapter-core": "^3.3.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@alcalzone/release-script": "^5.1.1",
|
|
31
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.1.2",
|
|
32
|
+
"@alcalzone/release-script-plugin-license": "^5.1.1",
|
|
33
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.1.1",
|
|
34
|
+
"@iobroker/adapter-dev": "^1.5.0",
|
|
35
|
+
"@iobroker/eslint-config": "^2.2.0",
|
|
36
|
+
"@iobroker/testing": "^5.2.2",
|
|
37
|
+
"@tsconfig/node18": "^18.2.6",
|
|
38
|
+
"@types/iobroker": "npm:@iobroker/types@^7.1.0",
|
|
39
|
+
"@types/node": "^18.19.130",
|
|
40
|
+
"rimraf": "^6.1.3",
|
|
41
|
+
"source-map-support": "^0.5.21",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "~5.9.3"
|
|
44
|
+
},
|
|
45
|
+
"main": "build/main.js",
|
|
46
|
+
"files": [
|
|
47
|
+
"admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).{json,json5}",
|
|
48
|
+
"admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
|
|
49
|
+
"build/",
|
|
50
|
+
"www/",
|
|
51
|
+
"io-package.json",
|
|
52
|
+
"LICENSE"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"prebuild": "rimraf build",
|
|
56
|
+
"build": "build-adapter ts",
|
|
57
|
+
"watch": "build-adapter ts --watch",
|
|
58
|
+
"prebuild:ts": "rimraf build",
|
|
59
|
+
"build:ts": "build-adapter ts",
|
|
60
|
+
"watch:ts": "build-adapter ts --watch",
|
|
61
|
+
"test:ts": "mocha --config test/mocharc.custom.json src/**/*.test.ts",
|
|
62
|
+
"test:package": "mocha test/package --exit",
|
|
63
|
+
"test:integration": "mocha test/integration --exit",
|
|
64
|
+
"test": "npm run test:ts && npm run test:package",
|
|
65
|
+
"check": "tsc --noEmit",
|
|
66
|
+
"lint": "eslint -c eslint.config.mjs .",
|
|
67
|
+
"translate": "translate-adapter",
|
|
68
|
+
"release": "release-script"
|
|
69
|
+
},
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/ipod86/ioBroker.script-restore/issues"
|
|
72
|
+
},
|
|
73
|
+
"readmeFilename": "README.md"
|
|
74
|
+
}
|