iobroker.script-restore 0.0.9 → 0.0.11

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/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 \"node:fs\";\nimport * as utils from \"@iobroker/adapter-core\";\nimport * as fs from \"node: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\";\nimport * as ftp from \"basic-ftp\";\nimport { Writable } from \"node:stream\";\nimport * as https from \"node:https\";\nimport * as http from \"node:http\";\nimport SftpClient from \"ssh2-sftp-client\";\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst SMB2 = require(\"@marsaud/smb2\");\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: 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\tthis.log.info(`Script Restore ready. Backup path: ${this.config.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\tcase \"getSourceConfig\":\n\t\t\t\t\tthis.sendTo(\n\t\t\t\t\t\tobj.from,\n\t\t\t\t\t\tobj.command,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlocalEnabled: this.config.localEnabled !== false,\n\t\t\t\t\t\t\tftpEnabled: !!this.config.ftpEnabled,\n\t\t\t\t\t\t\tsmbEnabled: !!this.config.smbEnabled,\n\t\t\t\t\t\t\thttpEnabled: !!this.config.httpEnabled,\n\t\t\t\t\t\t\tsftpEnabled: !!this.config.sftpEnabled,\n\t\t\t\t\t\t\twebdavEnabled: !!this.config.webdavEnabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tobj.callback,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"suggestBackupPath\":\n\t\t\t\t\tawait this.handleSuggestBackupPath(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseHttpUrl\":\n\t\t\t\t\tawait this.handleParseHttpUrl(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSftp\":\n\t\t\t\t\tawait this.handleTestSftp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSftpFiles\":\n\t\t\t\t\tawait this.handleListSftpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSftpFile\":\n\t\t\t\t\tawait this.handleParseSftpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testWebdav\":\n\t\t\t\t\tawait this.handleTestWebdav(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listWebdavFiles\":\n\t\t\t\t\tawait this.handleListWebdavFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseWebdavFile\":\n\t\t\t\t\tawait this.handleParseWebdavFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testFtp\":\n\t\t\t\t\tawait this.handleTestFtp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSmb\":\n\t\t\t\t\tawait this.handleTestSmb(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listFtpFiles\":\n\t\t\t\t\tawait this.handleListFtpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseFtpFile\":\n\t\t\t\t\tawait this.handleParseFtpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSmbFiles\":\n\t\t\t\t\tawait this.handleListSmbFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSmbFile\":\n\t\t\t\t\tawait this.handleParseSmbFile(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\t// \u2500\u2500\u2500 Local \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.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\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\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\t// \u2500\u2500\u2500 Tests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestFtp(obj: ioBroker.Message): Promise<void> {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\ttry {\n\t\t\tawait client.access({\n\t\t\t\thost: this.config.ftpHost,\n\t\t\t\tport: this.config.ftpPort || 21,\n\t\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\t\tsecure: this.config.ftpSecure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(this.config.ftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === ftp.FileType.File).length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.ftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleTestSmb(obj: ioBroker.Message): Promise<void> {\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, this.config.smbPath || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}${this.config.smbPath ? `\\\\${this.config.smbPath}` : \"\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 FTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createFtpClient(): ftp.Client {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\treturn client;\n\t}\n\n\tprivate async ftpConnect(client: ftp.Client): Promise<void> {\n\t\tawait client.access({\n\t\t\thost: this.config.ftpHost,\n\t\t\tport: this.config.ftpPort || 21,\n\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\tsecure: this.config.ftpSecure || false,\n\t\t});\n\t}\n\n\tprivate async handleListFtpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst remotePath = this.config.ftpPath || \"/\";\n\t\t\tconst list = await client.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(item => {\n\t\t\t\t\tconst n = item.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\titem.type === ftp.FileType.File &&\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(item => item.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleParseFtpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.ftpPath || \"/\", filename);\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst chunks: Buffer[] = [];\n\t\t\tconst writable = new Writable({\n\t\t\t\twrite(chunk, _enc, cb) {\n\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\n\t\t\t\t\tcb();\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait client.downloadTo(writable, remotePath);\n\t\t\tconst buf = Buffer.concat(chunks);\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} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SMB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createSmbClient(): typeof SMB2 {\n\t\treturn new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t}\n\n\tprivate smbReaddir(smb: typeof SMB2, dirPath: string): Promise<string[]> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readdir(dirPath, (err: Error | null, files: string[]) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(files);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate smbReadFile(smb: typeof SMB2, filePath: string): Promise<Buffer> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readFile(filePath, (err: Error | null, data: Buffer) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async handleListSmbFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst smbPath = this.config.smbPath || \"\";\n\t\t\tconst entries = await this.smbReaddir(smb, smbPath);\n\t\t\tconst files = entries\n\t\t\t\t.filter(n => {\n\t\t\t\t\treturn (\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.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: smbPath }, 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} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\tprivate async handleParseSmbFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst smbPath = this.config.smbPath || \"\";\n\t\tconst filePath = smbPath ? `${smbPath}\\\\${filename}` : filename;\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst buf = await this.smbReadFile(smb, 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} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\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\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, { withFileTypes: true, encoding: \"utf8\" })) 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\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\t// \u2500\u2500\u2500 Suggest backup path \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleSuggestBackupPath(obj: ioBroker.Message): Promise<void> {\n\t\tconst candidates = [\"/opt/iobroker/backups\", \"/root/backups\"];\n\t\t// Check if backupPC or iobroker-backup adapter is configured\n\t\ttry {\n\t\t\tconst backupObj = (await this.getForeignObjectAsync(\"system.adapter.backitup.0\")) as ioBroker.Object | null;\n\t\t\tif (backupObj?.native?.defaultFolder) {\n\t\t\t\tcandidates.unshift(backupObj.native.defaultFolder as string);\n\t\t\t}\n\t\t} catch {\n\t\t\t// adapter not installed\n\t\t}\n\t\tfor (const p of candidates) {\n\t\t\ttry {\n\t\t\t\tawait fs.access(p);\n\t\t\t\tthis.sendTo(obj.from, obj.command, p, obj.callback);\n\t\t\t\treturn;\n\t\t\t} catch {\n\t\t\t\t// not accessible\n\t\t\t}\n\t\t}\n\t\tthis.sendTo(obj.from, obj.command, \"\", obj.callback);\n\t}\n\n\t// \u2500\u2500\u2500 HTTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate downloadUrl(urlRaw: string): Promise<Buffer> {\n\t\tconst url = urlRaw.startsWith(\"http://\") || urlRaw.startsWith(\"https://\") ? urlRaw : `https://${urlRaw}`;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst mod = url.startsWith(\"https\") ? https : http;\n\t\t\tmod.get(url, res => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tres.on(\"data\", (c: Buffer) => chunks.push(c));\n\t\t\t\tres.on(\"end\", () => resolve(Buffer.concat(chunks)));\n\t\t\t\tres.on(\"error\", reject);\n\t\t\t}).on(\"error\", reject);\n\t\t});\n\t}\n\n\tprivate async handleParseHttpUrl(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.httpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"HTTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { url: string };\n\t\tconst filename = msg.url.split(\"/\").pop() || \"backup\";\n\t\ttry {\n\t\t\tconst buf = await this.downloadUrl(msg.url);\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\t// \u2500\u2500\u2500 SFTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestSftp(obj: ioBroker.Message): Promise<void> {\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst list = await sftp.list(this.config.sftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === \"-\").length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.sftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleListSftpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.sftpPath || \"/\";\n\t\t\tconst list = await sftp.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(i => {\n\t\t\t\t\tconst n = i.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"-\" &&\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(i => i.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleParseSftpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.sftpPath || \"/\", filename);\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst buf = (await sftp.get(remotePath)) as Buffer;\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} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 WebDAV \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestWebdav(obj: ioBroker.Message): Promise<void> {\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst list = await client.getDirectoryContents(this.config.webdavPath || \"/\");\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: unknown[] }).data;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${arr.length} Eintr\u00E4ge in: ${this.config.webdavPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\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 handleListWebdavFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.webdavPath || \"/\";\n\t\t\tconst list = await client.getDirectoryContents(remotePath);\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: { basename: string; type: string }[] }).data;\n\t\t\tconst files = arr\n\t\t\t\t.filter((i: { basename: string; type: string }) => {\n\t\t\t\t\tconst n = i.basename;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"file\" &&\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((i: { basename: string }) => i.basename)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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 handleParseWebdavFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = (this.config.webdavPath ? `${this.config.webdavPath}/` : \"/\") + filename;\n\t\t\tconst buf = Buffer.from((await client.getFileContents(remotePath)) as ArrayBuffer);\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\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 \"node: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\";\nimport * as ftp from \"basic-ftp\";\nimport { Writable } from \"node:stream\";\nimport * as https from \"node:https\";\nimport * as http from \"node:http\";\nimport SftpClient from \"ssh2-sftp-client\";\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst SMB2 = require(\"@marsaud/smb2\");\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: 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\tthis.log.info(`Script Restore ready. Backup path: ${this.config.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\tcase \"getSourceConfig\":\n\t\t\t\t\tthis.sendTo(\n\t\t\t\t\t\tobj.from,\n\t\t\t\t\t\tobj.command,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlocalEnabled: this.config.localEnabled !== false,\n\t\t\t\t\t\t\tftpEnabled: !!this.config.ftpEnabled,\n\t\t\t\t\t\t\tsmbEnabled: !!this.config.smbEnabled,\n\t\t\t\t\t\t\thttpEnabled: !!this.config.httpEnabled,\n\t\t\t\t\t\t\tsftpEnabled: !!this.config.sftpEnabled,\n\t\t\t\t\t\t\twebdavEnabled: !!this.config.webdavEnabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tobj.callback,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"suggestBackupPath\":\n\t\t\t\t\tawait this.handleSuggestBackupPath(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseHttpUrl\":\n\t\t\t\t\tawait this.handleParseHttpUrl(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSftp\":\n\t\t\t\t\tawait this.handleTestSftp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSftpFiles\":\n\t\t\t\t\tawait this.handleListSftpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSftpFile\":\n\t\t\t\t\tawait this.handleParseSftpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testWebdav\":\n\t\t\t\t\tawait this.handleTestWebdav(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listWebdavFiles\":\n\t\t\t\t\tawait this.handleListWebdavFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseWebdavFile\":\n\t\t\t\t\tawait this.handleParseWebdavFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testFtp\":\n\t\t\t\t\tawait this.handleTestFtp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSmb\":\n\t\t\t\t\tawait this.handleTestSmb(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listFtpFiles\":\n\t\t\t\t\tawait this.handleListFtpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseFtpFile\":\n\t\t\t\t\tawait this.handleParseFtpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSmbFiles\":\n\t\t\t\t\tawait this.handleListSmbFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSmbFile\":\n\t\t\t\t\tawait this.handleParseSmbFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"restoreScript\":\n\t\t\t\t\tawait this.handleRestoreScript(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\t// \u2500\u2500\u2500 Local \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.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\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\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\t// \u2500\u2500\u2500 Tests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestFtp(obj: ioBroker.Message): Promise<void> {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\ttry {\n\t\t\tawait client.access({\n\t\t\t\thost: this.config.ftpHost,\n\t\t\t\tport: this.config.ftpPort || 21,\n\t\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\t\tsecure: this.config.ftpSecure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(this.config.ftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === ftp.FileType.File).length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.ftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleTestSmb(obj: ioBroker.Message): Promise<void> {\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, this.config.smbPath || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}${this.config.smbPath ? `\\\\${this.config.smbPath}` : \"\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 FTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createFtpClient(): ftp.Client {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\treturn client;\n\t}\n\n\tprivate async ftpConnect(client: ftp.Client): Promise<void> {\n\t\tawait client.access({\n\t\t\thost: this.config.ftpHost,\n\t\t\tport: this.config.ftpPort || 21,\n\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\tsecure: this.config.ftpSecure || false,\n\t\t});\n\t}\n\n\tprivate async handleListFtpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst remotePath = this.config.ftpPath || \"/\";\n\t\t\tconst list = await client.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(item => {\n\t\t\t\t\tconst n = item.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\titem.type === ftp.FileType.File &&\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(item => item.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleParseFtpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.ftpPath || \"/\", filename);\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst chunks: Buffer[] = [];\n\t\t\tconst writable = new Writable({\n\t\t\t\twrite(chunk, _enc, cb) {\n\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\n\t\t\t\t\tcb();\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait client.downloadTo(writable, remotePath);\n\t\t\tconst buf = Buffer.concat(chunks);\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} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SMB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createSmbClient(): typeof SMB2 {\n\t\treturn new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t}\n\n\tprivate smbReaddir(smb: typeof SMB2, dirPath: string): Promise<string[]> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readdir(dirPath, (err: Error | null, files: string[]) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(files);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate smbReadFile(smb: typeof SMB2, filePath: string): Promise<Buffer> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readFile(filePath, (err: Error | null, data: Buffer) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async handleListSmbFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst smbPath = this.config.smbPath || \"\";\n\t\t\tconst entries = await this.smbReaddir(smb, smbPath);\n\t\t\tconst files = entries\n\t\t\t\t.filter(n => {\n\t\t\t\t\treturn (\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.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: smbPath }, 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} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\tprivate async handleParseSmbFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst smbPath = this.config.smbPath || \"\";\n\t\tconst filePath = smbPath ? `${smbPath}\\\\${filename}` : filename;\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst buf = await this.smbReadFile(smb, 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} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\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\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, { withFileTypes: true, encoding: \"utf8\" })) 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\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\t// \u2500\u2500\u2500 Suggest backup path \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleSuggestBackupPath(obj: ioBroker.Message): Promise<void> {\n\t\tconst candidates = [\"/opt/iobroker/backups\", \"/root/backups\"];\n\t\t// Check if backupPC or iobroker-backup adapter is configured\n\t\ttry {\n\t\t\tconst backupObj = (await this.getForeignObjectAsync(\"system.adapter.backitup.0\")) as ioBroker.Object | null;\n\t\t\tif (backupObj?.native?.defaultFolder) {\n\t\t\t\tcandidates.unshift(backupObj.native.defaultFolder as string);\n\t\t\t}\n\t\t} catch {\n\t\t\t// adapter not installed\n\t\t}\n\t\tfor (const p of candidates) {\n\t\t\ttry {\n\t\t\t\tawait fs.access(p);\n\t\t\t\tthis.sendTo(obj.from, obj.command, p, obj.callback);\n\t\t\t\treturn;\n\t\t\t} catch {\n\t\t\t\t// not accessible\n\t\t\t}\n\t\t}\n\t\tthis.sendTo(obj.from, obj.command, \"\", obj.callback);\n\t}\n\n\t// \u2500\u2500\u2500 HTTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate downloadUrl(urlRaw: string): Promise<Buffer> {\n\t\tconst url = urlRaw.startsWith(\"http://\") || urlRaw.startsWith(\"https://\") ? urlRaw : `https://${urlRaw}`;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst mod = url.startsWith(\"https\") ? https : http;\n\t\t\tmod.get(url, res => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tres.on(\"data\", (c: Buffer) => chunks.push(c));\n\t\t\t\tres.on(\"end\", () => resolve(Buffer.concat(chunks)));\n\t\t\t\tres.on(\"error\", reject);\n\t\t\t}).on(\"error\", reject);\n\t\t});\n\t}\n\n\tprivate async handleParseHttpUrl(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.httpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"HTTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { url: string };\n\t\tconst filename = msg.url.split(\"/\").pop() || \"backup\";\n\t\ttry {\n\t\t\tconst buf = await this.downloadUrl(msg.url);\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\t// \u2500\u2500\u2500 SFTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestSftp(obj: ioBroker.Message): Promise<void> {\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst list = await sftp.list(this.config.sftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === \"-\").length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.sftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleListSftpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.sftpPath || \"/\";\n\t\t\tconst list = await sftp.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(i => {\n\t\t\t\t\tconst n = i.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"-\" &&\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(i => i.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleParseSftpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.sftpPath || \"/\", filename);\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst buf = (await sftp.get(remotePath)) as Buffer;\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} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 WebDAV \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestWebdav(obj: ioBroker.Message): Promise<void> {\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst list = await client.getDirectoryContents(this.config.webdavPath || \"/\");\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: unknown[] }).data;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${arr.length} Eintr\u00E4ge in: ${this.config.webdavPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\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 handleListWebdavFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.webdavPath || \"/\";\n\t\t\tconst list = await client.getDirectoryContents(remotePath);\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: { basename: string; type: string }[] }).data;\n\t\t\tconst files = arr\n\t\t\t\t.filter((i: { basename: string; type: string }) => {\n\t\t\t\t\tconst n = i.basename;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"file\" &&\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((i: { basename: string }) => i.basename)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, 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 handleParseWebdavFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = (this.config.webdavPath ? `${this.config.webdavPath}/` : \"/\") + filename;\n\t\t\tconst buf = Buffer.from((await client.getFileContents(remotePath)) as ArrayBuffer);\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\t// \u2500\u2500\u2500 Restore to ioBroker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleRestoreScript(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { path: string; name: string; type: string; source: string; suffix: string };\n\t\tconst suffix = msg.suffix || \"_rcvr\";\n\n\t\t// Append suffix to the last path segment (same logic as scriptRecovery.js)\n\t\tconst parts = msg.path.split(\".\");\n\t\tparts[parts.length - 1] = parts[parts.length - 1] + suffix;\n\t\tconst newScriptPath = parts.join(\".\");\n\t\tconst newId = `script.js.${newScriptPath}`;\n\t\tconst newName = msg.name + suffix;\n\n\t\t// Do not overwrite existing scripts\n\t\tlet existing: ioBroker.Object | null | undefined;\n\t\ttry {\n\t\t\texisting = await this.getForeignObjectAsync(newId);\n\t\t} catch {\n\t\t\texisting = null;\n\t\t}\n\t\tif (existing) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: `Skript existiert bereits: ${newId}` }, obj.callback);\n\t\t\treturn;\n\t\t}\n\n\t\tconst engineTypeMap: Record<string, string> = {\n\t\t\tTypeScript: \"TypeScript/ts\",\n\t\t\tBlockly: \"Blockly\",\n\t\t\tRules: \"Rules\",\n\t\t\tJS: \"JavaScript/js\",\n\t\t};\n\t\tconst engineType = engineTypeMap[msg.type] || \"JavaScript/js\";\n\n\t\ttry {\n\t\t\tawait this.ensureScriptFolders(newId);\n\t\t\tawait this.setForeignObjectAsync(newId, {\n\t\t\t\ttype: \"script\",\n\t\t\t\tcommon: {\n\t\t\t\t\tname: newName,\n\t\t\t\t\tengineType,\n\t\t\t\t\tengine: \"system.adapter.javascript.0\",\n\t\t\t\t\tsource: msg.source || \"\",\n\t\t\t\t\tenabled: false,\n\t\t\t\t\tdebug: false,\n\t\t\t\t\tverbose: false,\n\t\t\t\t} as unknown as ioBroker.ScriptCommon,\n\t\t\t\tnative: {},\n\t\t\t});\n\t\t\tthis.log.info(`Script restored: ${newId}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { success: true, id: newId }, 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 ensureScriptFolders(scriptId: string): Promise<void> {\n\t\t// scriptId = \"script.js.folderA.folderB.scriptName\"\n\t\t// Create folder objects for each intermediate path segment\n\t\tconst parts = scriptId.split(\".\");\n\t\tfor (let i = 2; i < parts.length - 1; i++) {\n\t\t\tconst folderId = parts.slice(0, i + 1).join(\".\");\n\t\t\ttry {\n\t\t\t\tconst existing = await this.getForeignObjectAsync(folderId);\n\t\t\t\tif (!existing) {\n\t\t\t\t\tawait this.setForeignObjectAsync(folderId, {\n\t\t\t\t\t\ttype: \"folder\",\n\t\t\t\t\t\tcommon: { name: parts[i] } as ioBroker.ObjectCommon,\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore individual folder creation errors\n\t\t\t}\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": ["SftpClient"]
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "script-restore",
4
- "version": "0.0.9",
4
+ "version": "0.0.11",
5
5
  "news": {
6
+ "0.0.11": {
7
+ "en": "add type filter (JS/TS/Blockly/Rules) in script sidebar\nadd direct restore into ioBroker with suffix input and confirm modal\nremove obsolete admin/words.js and .prettierignore",
8
+ "de": "typfilter (JS/TS/Blockly/Rules) in Skript-Seitenleiste hinzufügen\ndirekte Wiederherstellung in ioBroker mit Suffix-Eingang und bestätigen\nentfernen obsolete admin/words.js und .prettierignore",
9
+ "ru": "добавить фильтр типа (JS/TS/Blockly/Rules) на боковой панели сценария\nдобавить прямое восстановление в ioBroker с вводом суффикса и подтвердить модаль\nудалить устаревшие admin/words.js и .prettierignore",
10
+ "pt": "adicionar filtro do tipo (JS/TS/Blockly/Regras) na barra lateral do script\nadicionar restauração direta no ioBroker com entrada sufixo e confirmar modal\nremover o obsoleto admin/words.js e .prettierignore",
11
+ "nl": "typefilter (JS/TS/Blockly/Rules) toevoegen in script zijbalk\ndirect herstellen toevoegen aan ioBroker met achtervoegsel ingang en bevestigen modal\nverouderde admin/words.js en .prettierignore verwijderen",
12
+ "fr": "ajouter un filtre de type (JS/TS/Blockly/Rules) dans la barre latérale du script\najouter la restauration directe dans ioBroker avec suffixe entrée et confirmer modal\nsupprimer admin/words.js obsolète et .prettierignore",
13
+ "it": "aggiungere il filtro di tipo (JS/TS/Blockly/Rules) nella barra laterale dello script\naggiungere il ripristino diretto in ioBroker con ingresso suffisso e confermare modal\nrimuovere obsoleti admin/words.js e .prettierignore",
14
+ "es": "añadir filtro tipo (JS/TS/Blockly/Rules) en barra lateral de script\nañadir la restauración directa en ioBroker con entrada de sufijo y confirmar modal\neliminar obsoletos admin/words.js y .prettierignore",
15
+ "pl": "dodaj filtr typu (JS / TS / Blockly / Rules) w pasku bocznym skryptu\ndodaj bezpośrednie przywracanie do joBrokera z wejściem przyrostowym i potwierdź modal\nusunąć przestarzałe admin / words.js i .prettierignore",
16
+ "uk": "додати фільтр типу (JS/TS/Blockly/Rules) в бічній панелі скриптів\nдодати прямий відновлення в ioBroker з входом Suffix і підтвердити модаль\nвидалити obsolete admin/words.js і .prettierignore",
17
+ "zh-cn": "在脚本边栏中添加类型过滤器( JS/ TS/ Blockly/ Rules)\n在 ioBroker 中添加带有后缀输入的直接还原并确认模式\n删除过时的管理员/名词.js和. pretierignore"
18
+ },
19
+ "0.0.10": {
20
+ "en": "Fix jsonConfig responsive sizes (E5509); trim news to 7 entries; add Dependabot cooldown",
21
+ "de": "jsonConfig Responsive-Größen korrigiert (E5509); News auf 7 Einträge reduziert; Dependabot-Cooldown ergänzt",
22
+ "ru": "Исправлены размеры jsonConfig; сокращены новости до 7; добавлен cooldown Dependabot",
23
+ "pt": "Corrigidos tamanhos responsivos jsonConfig; notícias reduzidas a 7; cooldown Dependabot adicionado",
24
+ "nl": "jsonConfig responsieve maten gecorrigeerd; nieuws beperkt tot 7; Dependabot cooldown toegevoegd",
25
+ "fr": "Correction tailles responsives jsonConfig; actualités réduites à 7; cooldown Dependabot ajouté",
26
+ "it": "Corrette dimensioni responsive jsonConfig; notizie ridotte a 7; aggiunto cooldown Dependabot",
27
+ "es": "Corregidos tamaños responsivos jsonConfig; noticias reducidas a 7; añadido cooldown Dependabot",
28
+ "pl": "Poprawiono rozmiary responsywne jsonConfig; aktualności ograniczone do 7; dodano cooldown Dependabot",
29
+ "uk": "Виправлено розміри jsonConfig; новини скорочено до 7; додано cooldown Dependabot",
30
+ "zh-cn": "修复 jsonConfig 响应式尺寸;新闻条目减少至 7 条;添加 Dependabot 冷却时间"
31
+ },
6
32
  "0.0.9": {
7
33
  "en": "Fix jsonConfig: add responsive size attributes, i18n support, remove outdated admin files",
8
34
  "de": "jsonConfig: Responsive Size-Attribute ergänzt, i18n-Unterstützung, veraltete Admin-Dateien entfernt",
@@ -67,45 +93,6 @@
67
93
  "pl": "Dodano FTP/SMB jako opcjonalne źródła kopii zapasowych z przyciskiem testowym; lokalne źródło opcjonalne",
68
94
  "uk": "Додано FTP/SMB як опціональні джерела резервних копій з кнопкою тестування; локальне джерело опціональне",
69
95
  "zh-cn": "添加 FTP/SMB 作为可选备份来源(含测试按钮);本地来源可选"
70
- },
71
- "0.0.4": {
72
- "en": "Improve dark theme detection: live switching via MutationObserver and storage events",
73
- "de": "Dark-Theme-Erkennung verbessert: Live-Wechsel per MutationObserver und Storage-Events",
74
- "ru": "Улучшено определение тёмной темы: переключение в реальном времени через MutationObserver и storage-события",
75
- "pt": "Melhoria na detecção do tema escuro: troca em tempo real via MutationObserver e eventos de armazenamento",
76
- "nl": "Verbeterde detectie van donker thema: live wisseling via MutationObserver en opslag-events",
77
- "fr": "Amélioration de la détection du thème sombre : changement en direct via MutationObserver et événements de stockage",
78
- "it": "Migliorata la rilevazione del tema scuro: cambio in tempo reale tramite MutationObserver ed eventi di archiviazione",
79
- "es": "Mejora en la detección del tema oscuro: cambio en vivo mediante MutationObserver y eventos de almacenamiento",
80
- "pl": "Ulepszone wykrywanie ciemnego motywu: przełączanie na żywo przez MutationObserver i zdarzenia storage",
81
- "uk": "Покращено визначення темної теми: перемикання в реальному часі через MutationObserver та події сховища",
82
- "zh-cn": "改进暗色主题检测:通过 MutationObserver 和存储事件实现实时切换"
83
- },
84
- "0.0.3": {
85
- "en": "Add dark theme support for admin tab UI",
86
- "de": "Dark-Theme-Unterstützung für die Admin-Tab-Oberfläche hinzugefügt",
87
- "ru": "Добавлена поддержка тёмной темы для вкладки администратора",
88
- "pt": "Adicionado suporte ao tema escuro para a interface da aba admin",
89
- "nl": "Ondersteuning voor donker thema toegevoegd aan de admin-tab UI",
90
- "fr": "Ajout du support du thème sombre pour l'interface de l'onglet admin",
91
- "it": "Aggiunto supporto al tema scuro per l'interfaccia della scheda admin",
92
- "es": "Añadido soporte de tema oscuro para la interfaz de la pestaña admin",
93
- "pl": "Dodano obsługę ciemnego motywu dla interfejsu karty administratora",
94
- "uk": "Додано підтримку темної теми для інтерфейсу вкладки адміністратора",
95
- "zh-cn": "为管理员选项卡界面添加暗色主题支持"
96
- },
97
- "0.0.1": {
98
- "en": "Initial release",
99
- "de": "Erstveröffentlichung",
100
- "ru": "Начальная версия",
101
- "pt": "Versão inicial",
102
- "nl": "Eerste versie",
103
- "fr": "Version initiale",
104
- "it": "Versione iniziale",
105
- "es": "Versión inicial",
106
- "pl": "Pierwsze wydanie",
107
- "uk": "Початкова версія",
108
- "zh-cn": "首次出版"
109
96
  }
110
97
  },
111
98
  "titleLang": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.script-restore",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Restore ioBroker scripts from backup archives",
5
5
  "author": {
6
6
  "name": "ipod86",
@@ -27,7 +27,7 @@
27
27
  "dependencies": {
28
28
  "@iobroker/adapter-core": "^3.3.2",
29
29
  "@marsaud/smb2": "^0.18.0",
30
- "basic-ftp": "^5.2.0",
30
+ "basic-ftp": "^5.2.2",
31
31
  "ssh2-sftp-client": "^12.1.1",
32
32
  "webdav": "^5.9.0"
33
33
  },
@@ -41,7 +41,7 @@
41
41
  "@iobroker/testing": "^5.2.2",
42
42
  "@tsconfig/node18": "^18.2.6",
43
43
  "@types/iobroker": "npm:@iobroker/types@^7.1.0",
44
- "@types/node": "^25.5.2",
44
+ "@types/node": "^25.6.0",
45
45
  "@types/ssh2-sftp-client": "^9.0.6",
46
46
  "rimraf": "^6.1.3",
47
47
  "source-map-support": "^0.5.21",