UNPKG

iobroker.backitup

Version:

ioBroker.backitup allows you to backup and restore your ioBroker installation and other systems, such as databases, Zigbee, scripts and many more.

1,007 lines (897 loc) 107 kB
/* jshint -W097 */ /* jshint strict: false */ /* jslint node: true */ 'use strict'; const fs = require('node:fs'); const path = require('node:path'); const utils = require('@iobroker/adapter-core'); // Get common adapter utils const schedule = require('node-schedule'); const tools = require('./lib/tools'); const executeScripts = require('./lib/execute'); const systemCheck = require('./lib/systemCheck'); const TokenRefresher = require('./lib/tokenRefresher'); const adapterName = require('./package.json').name.split('.').pop(); let adapter; let timerOutput; let timerOutput2; let timerUmount1; let timerUmount2; let timerMain; let slaveTimeOut; let waitToSlaveBackup; let dlServer; let ulServer; let http; let https; let systemLang = 'de'; // system language const backupConfig = {}; const backupTimeSchedules = []; // Array for Backup Times let taskRunning = false; let dropBoxTokenRefresher = null; const bashDir = path.join(utils.getAbsoluteDefaultDataDir(), adapterName).replace(/\\/g, '/'); /** * Decrypt the password/value with given key * * @param key - Secret key * @param value - value to decrypt * @returns */ function decrypt(key, value) { let result = ''; for (let i = 0; i < value.length; i++) { result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i)); } return result; } async function updateAccessTokens(config) { if (dropBoxTokenRefresher) { try { const accessToken = await dropBoxTokenRefresher.getAccessToken(); Object.keys(config).forEach((key) => { if (config[key] && typeof config[key] === 'object') { if (config[key].dropbox) { config[key].dropbox.accessToken = accessToken; } else { Object.keys(config[key]).forEach((subKey) => { if (config[key][subKey] && config[key][subKey].dropbox) { config[key][subKey].dropbox.accessToken = accessToken; } }); } } }); } catch (e) { adapter.log.error(`Cannot get access tokens for DropBox: ${e}`); } } } async function startBackup(config, cb) { if (taskRunning) { return setTimeout(startBackup, 10000, config, cb); } // await updateAccessTokens(config); taskRunning = true; try { executeScripts(adapter, config, err => { taskRunning = false; cb && cb(err); }); adapter.log.debug('Backup has started ...'); } catch (e) { adapter.log.warn(`Backup error: ${e.stack}`); adapter.log.warn(`Backup error: ${e} ... please check your config and and try again!!`); } } function startAdapter(options) { options = options || {}; Object.assign(options, { name: adapterName }); adapter = new utils.Adapter(options); adapter.on('stateChange', async (id, state) => { dropBoxTokenRefresher?.onStateChange(id, state); if (id === `${adapter.namespace}.info.dropboxTokens`) { await updateAccessTokens(backupConfig); adapter.log.debug('Config Update for Dropbox Token'); } if (state && (state.val === true || state.val === 'true') && !state.ack) { if (id === `${adapter.namespace}.oneClick.iobroker` || id === `${adapter.namespace}.oneClick.ccu` ) { const sysCheck = await systemCheck.storageSizeCheck(adapter, adapterName, adapter.log); const type = id.split('.').pop(); if ((sysCheck && sysCheck.ready && sysCheck.ready === true) || adapter.config.cifsEnabled === true) { let config; try { config = JSON.parse(JSON.stringify(backupConfig[type])); config.enabled = true; config.deleteBackupAfter = 0; // do not delete files by custom backup } catch (e) { adapter.log.warn(`backup error: ${e.stack}`); adapter.log.warn(`backup error: ${e} ... please check your config and try again!!`); } startBackup(config, err => { if (err) { adapter.log.error(`[${type}] ${err}`); } else { adapter.log.debug(`[${type}] exec: done`); } timerOutput = setTimeout(() => { return adapter.getState('output.line', (err, state) => { if (state && state.val === '[EXIT] 0') { adapter.setState(`history.${type}Success`, true, true); adapter.setState(`history.${type}LastTime`, tools.getTimeString(systemLang), true); if (adapter.config.onedriveEnabled && adapter.config.hostType === 'Single') { renewOnedriveToken(); } } else { adapter.setState(`history.${type}LastTime`, `error: ${tools.getTimeString(systemLang)}`, true); adapter.setState(`history.${type}Success`, false, true); } }); }, 500); adapter.setState(`oneClick.${type}`, false, true); if (adapter.config.slaveInstance && type === 'iobroker' && adapter.config.hostType === 'Master') { adapter.log.debug('Slave backup from BackItUp-Master is started ...'); startSlaveBackup(adapter.config.slaveInstance[0], null); } }); } else { adapter.log.error(`A local backup is currently not possible. The storage space is currently only ${sysCheck && sysCheck.diskFree ? sysCheck.diskFree : null} MB`); systemCheck.systemMessage(adapter, tools._('A local backup is currently not possible. Please check your System!', systemLang)); adapter.setState(`oneClick.${type}`, false, true); adapter.setState('output.line', `[EXIT] ${tools._('A local backup is currently not possible. Please check your System!', systemLang)}`, true); } } } }); adapter.on('ready', async () => { try { await main(adapter); } catch (e) { //ignore errors } }); // is called when adapter shuts down - callback has to be called under any circumstances! adapter.on('unload', (callback) => { try { dropBoxTokenRefresher?.destroy(); adapter.log.info('cleaned everything up...'); timerOutput2 && clearTimeout(timerOutput2); timerOutput && clearTimeout(timerOutput); timerUmount1 && clearTimeout(timerUmount1); timerUmount2 && clearTimeout(timerUmount2); timerMain && clearTimeout(timerMain); slaveTimeOut && clearTimeout(slaveTimeOut); waitToSlaveBackup && clearTimeout(waitToSlaveBackup); if (dlServer) { try { dlServer.closeAllConnections(); } catch (e) { adapter.log.debug(`Download server Connections could not be closed: ${e}`); } try { dlServer.close(); } catch (e) { adapter.log.debug(`Download server Connections could not be closed: ${e}`); } } if (ulServer) { try { ulServer.closeAllConnections(); } catch (e) { adapter.log.debug(`Upload server connections could not be closed: ${e}`); } try { ulServer.close(); } catch (e) { adapter.log.debug(`Upload server connections could not be closed: ${e}`); } } } catch (e) { console.log(`Cannot unload: ${e}`); } callback(); }); adapter.on('message', async obj => { if (obj) { switch (obj.command) { case 'list': try { const list = require('./lib/list'); adapter.log.debug(`Reading backup list...`); await updateAccessTokens(backupConfig); list(obj.message, backupConfig, adapter.log, res => { adapter.log.debug(`Backup list was read: ${JSON.stringify(res)}`); obj.callback && adapter.sendTo(obj.from, obj.command, res, obj.callback); }); } catch (e) { adapter.log.debug('Backup list cannot be read ...'); } break; case 'authGoogleDrive': const GoogleDrive = require('./lib/googleDriveLib'); if (obj.callback) { const google = new GoogleDrive(); google.getAuthorizeUrl() .then(url => adapter.sendTo(obj.from, obj.command, { url }, obj.callback)); } break; case 'authDropbox': if (obj.callback) { TokenRefresher.getAuthUrl('https://oauth2.iobroker.in/dropbox') .then(url => adapter.sendTo(obj.from, obj.command, { url }, obj.callback)) } break; case 'authOnedrive': const Onedrive = require('./lib/oneDriveLib'); if (obj.message && obj.message.code) { const onedrive = new Onedrive(); onedrive.getRefreshToken(obj.message.code, adapter.log) .then(json => adapter.sendTo(obj.from, obj.command, { done: true, json }, obj.callback)) .catch(err => adapter.sendTo(obj.from, obj.command, { error: err }, obj.callback)); } else if (obj.callback) { const onedrive = new Onedrive(); onedrive.getAuthorizeUrl(adapter.log) .then(url => adapter.sendTo(obj.from, obj.command, { url: url }, obj.callback)) .catch(err => adapter.sendTo(obj.from, obj.command, { error: err }, obj.callback)); } break; case 'restore': if (obj.message) { if (obj.message.stopIOB) { await getCerts(obj.from); } adapter.log.info(`DATA: ${JSON.stringify(obj.message)}`); await updateAccessTokens(backupConfig); const _restore = require('./lib/restore'); _restore.restore( adapter, backupConfig, obj.message.type, obj.message.fileName, obj.message.currentTheme, obj.message.currentProtocol, bashDir, adapter.log, res => obj.callback && adapter.sendTo(obj.from, obj.command, res, obj.callback), ); } else if (obj.callback) { obj.callback({ error: 'Invalid parameters' }); } break; case 'uploadFile': if (obj.message && obj.message.protocol) { if (ulServer && ulServer._connectionKey && ulServer.listening) { adapter.log.debug(`Upload server is running on Port ${ulServer.address().port}...`); } else { if (obj.message.protocol === 'https:') { await getCerts(obj.from); } try { ulFileServer(obj.message.protocol); } catch (e) { adapter.log.debug('Upload server cannot started'); } } try { adapter.sendTo(obj.from, obj.command, { listenPort: ulServer.address().port }, obj.callback); } catch (e) { adapter.sendTo(obj.from, obj.command, { e }, obj.callback); } } else if (obj.callback) { obj.callback({ error: 'Invalid parameters' }); } break; case 'getFile': if (obj.message && obj.message.type && obj.message.fileName && obj.message.protocol) { if (dlServer && dlServer._connectionKey && dlServer.listening) { adapter.log.debug(`Download server is running on port ${dlServer.address().port}...`); } else { if (obj.message.protocol === 'https:') { await getCerts(obj.from); } try { dlFileServer(obj.message.protocol); } catch (e) { adapter.log.debug('Downloadserver cannot started'); } } const fileName = obj.message.fileName.split('/').pop(); if (obj.message.type !== 'local') { const backupDir = path.join(tools.getIobDir(), 'backups'); const toSaveName = path.join(backupDir, fileName); const _getFile = require('./lib/restore'); await updateAccessTokens(backupConfig); _getFile.getFile(backupConfig, obj.message.type, obj.message.fileName, toSaveName, adapter.log, err => { if (!err && fs.existsSync(toSaveName)) { try { adapter.sendTo(obj.from, obj.command, { fileName: fileName, listenPort: dlServer.address().port }, obj.callback); } catch (error) { adapter.sendTo(obj.from, obj.command, { error }, obj.callback); } } else { adapter.log.warn(`File ${toSaveName} not found`); } }); } else if (fs.existsSync(obj.message.fileName)) { try { adapter.sendTo(obj.from, obj.command, { fileName: fileName, listenPort: dlServer.address().port }, obj.callback); } catch (error) { adapter.sendTo(obj.from, obj.command, { error }, obj.callback); } } } else if (obj.callback) { obj.callback({ error: 'Invalid parameters' }); } break; case 'serverClose': if (obj.message && obj.message.downloadFinish && !obj.message.uploadFinish) { adapter.log.debug('Download finished...'); adapter.sendTo(obj.from, obj.command, { serverClose: true }, obj.callback); } else if (obj.message && obj.message.uploadFinish && !obj.message.downloadFinish) { adapter.log.debug('Upload finished...'); adapter.sendTo(obj.from, obj.command, { serverClose: true }, obj.callback); } else if (obj.callback) { obj.callback({ error: 'Invalid parameters' }); } break; case 'getTelegramUser': if (obj && obj.message) { const inst = obj.message.config.instance ? obj.message.config.instance : adapter.config.telegramInstance; adapter.getForeignState(`${inst}.communicate.users`, (err, state) => { err && adapter.log.error(err); if (state && state.val) { try { adapter.sendTo(obj.from, obj.command, state.val, obj.callback); } catch (err) { err && adapter.log.error(err); adapter.log.error('Cannot parse stored user IDs from Telegram!'); } } }); } break; case 'getSystemInfo': if (obj) { let systemInfo = process.platform;; let dbInfo = false; if (fs.existsSync('/opt/scripts/.docker_config/.thisisdocker')) { // Docker Image Support >= 5.2.0 systemInfo = 'docker'; if (fs.existsSync('/opt/scripts/.docker_config/.backitup')) { dbInfo = true; } } else { const isWin = process.platform.startsWith('win'); if (isWin) { systemInfo = 'win'; } } try { adapter.sendTo(obj.from, obj.command, { systemOS: systemInfo, dockerDB: dbInfo, backupDir: path.join(tools.getIobDir(), 'backups') }, obj.callback); } catch (err) { err && adapter.log.error(err); } } break; case 'getFileSystemInfo': if (obj) { const sysCheck = await systemCheck.storageSizeCheck(adapter, adapterName, adapter.log); if (sysCheck) { try { adapter.sendTo(obj.from, obj.command, sysCheck, obj.callback); } catch (err) { err && adapter.log.error(err); } } } break; case 'testWebDAV': if (obj.message) { const { createClient } = await import('webdav'); const agent = new (require('node:https').Agent)({ rejectUnauthorized: Boolean(obj.message.config.signedCertificates) }); const client = createClient( obj.message.config.host, { username: obj.message.config.username, password: obj.message.config.password, maxBodyLength: Infinity, httpsAgent: agent }); client .getDirectoryContents('') .then(contents => obj.callback && adapter.sendTo(obj.from, obj.command, contents, obj.callback)) .catch(err => adapter.sendTo(obj.from, obj.command, { error: JSON.stringify(err.message) }, obj.callback)); } break; case 'slaveBackup': if (obj?.message) { if (adapter.config.hostType === 'Slave') { adapter.log.debug('Slave Backup started ...'); const type = 'iobroker'; let config; try { config = JSON.parse(JSON.stringify(backupConfig[type])); config.enabled = true; config.deleteBackupAfter = obj.message.config.deleteAfter ? obj.message.config.deleteAfter : 0; // do delete files with specification from Master } catch (e) { adapter.log.warn(`backup error: ${e} ... please check your config and try again!!`); } startBackup(config, err => { if (err) { adapter.log.error(`[${type}] ${err}`); } else { adapter.log.debug(`[${type}] exec: done`); } timerOutput = setTimeout(() => adapter.getState('output.line', (err, state) => { if (state && state.val === '[EXIT] 0') { adapter.setState(`history.${type}Success`, true, true); adapter.setState(`history.${type}LastTime`, tools.getTimeString(systemLang), true); try { adapter.sendTo(obj.from, obj.command, state.val, obj.callback); } catch (err) { err && adapter.log.error(err); adapter.log.error('slave Backup not finish!'); } if (adapter.config.onedriveEnabled) { renewOnedriveToken(); } } else { adapter.setState(`history.${type}LastTime`, `error: ${tools.getTimeString(systemLang)}`, true); adapter.setState(`history.${type}Success`, false, true); if (state && state.val) { try { adapter.sendTo(obj.from, obj.command, state.val, obj.callback); } catch (err) { err && adapter.log.error(err); adapter.log.error('slave Backup not finish!'); } } } }), 500); adapter.setState(`oneClick.${type}`, false, true); }); } else { adapter.log.warn('Your BackItUp Instance is not configured as a slave'); adapter.sendTo(obj.from, obj.command, 'not configured as a slave', obj.callback); } } break; case 'slaveInstance': if (obj && obj.command === 'slaveInstance' && obj.message && obj.message.instance) { let resultInstances = []; const instances = await adapter.getObjectViewAsync('system', 'instance', { startkey: `system.adapter.${obj.message.instance}.`, endkey: `system.adapter.${obj.message.instance}.\u9999`, }).catch((err) => adapter.log.error(err)); if (instances && instances.rows && instances.rows.length != 0) { instances.rows.forEach(async (row) => { if (row.id.replace('system.adapter.', '') != adapter.namespace) { resultInstances.push({ label: row.id.replace('system.adapter.', ''), value: row.id.replace('system.adapter.', ''), }); } }); } adapter.sendTo(obj.from, obj.command, resultInstances, obj.callback); } break; case 'getLog': const logName = path.join(bashDir, `${adapter.namespace}.log`).replace(/\\/g, '/'); if (fs.existsSync(logName) && (obj?.message.backupName || obj?.message.timestamp)) { const data = fs.readFileSync(logName, 'utf8'); const backupLog = JSON.parse(data); const backupName = obj?.message.backupName ? obj.message.backupName : null; const timestamp = obj?.message.timestamp; let found = false; backupLog.forEach((item, index) => { if (item.hasOwnProperty(timestamp)) { found = true; adapter.log.debug(`Printing logs of previous backup`); adapter.sendTo(obj.from, obj.command, item[timestamp], obj.callback); } else if (backupName !== null && item.hasOwnProperty(backupName)) { found = true; adapter.log.debug(`Printing logs of previous backup`); adapter.sendTo(obj.from, obj.command, item[backupName], obj.callback); } else if ((backupLog.length - 1) == index && !found) { adapter.log.debug(`No Backuplogs found`); adapter.sendTo(obj.from, obj.command, tools._('No log is available for this backup', systemLang), obj.callback); } }); } break; } } }); return adapter; } async function checkStates() { // Fill empty data points with default values const historyState = await adapter.getStateAsync('history.html'); if (!historyState || historyState.val === null) { await adapter.setStateAsync('history.html', { val: `<span class="backup-type-total">${tools._('No backups yet', systemLang)}</span>`, ack: true }); } const iobrokerLastTime = await adapter.getStateAsync('history.iobrokerLastTime'); if (!iobrokerLastTime || iobrokerLastTime.val === null) { await adapter.setStateAsync('history.iobrokerLastTime', { val: tools._('No backups yet', systemLang), ack: true }); } const ccuLastTime = await adapter.getStateAsync('history.ccuLastTime'); if (!ccuLastTime || ccuLastTime.val === null) { await adapter.setStateAsync('history.ccuLastTime', { val: tools._('No backups yet', systemLang), ack: true }); } const iobrokerState = await adapter.getStateAsync('oneClick.iobroker'); if (!iobrokerState || iobrokerState.val === null || iobrokerState.val === true) { await adapter.setStateAsync('oneClick.iobroker', { val: false, ack: true }); } const ccuState = await adapter.getStateAsync('oneClick.ccu'); if (!ccuState || ccuState.val === null || ccuState.val === true) { await adapter.setStateAsync('oneClick.ccu', { val: false, ack: true }); } const ccuSuccess = await adapter.getStateAsync('history.ccuSuccess'); if (!ccuSuccess || ccuSuccess.val === null) { await adapter.setStateAsync('history.ccuSuccess', { val: false, ack: true }); } const iobrokerSuccess = await adapter.getStateAsync('history.iobrokerSuccess'); if (!iobrokerSuccess || iobrokerSuccess.val === null) { await adapter.setStateAsync('history.iobrokerSuccess', { val: false, ack: true }); } const jsonState = await adapter.getStateAsync('history.json'); if (!jsonState || jsonState.val === null) { await adapter.setStateAsync('history.json', { val: '[]', ack: true }); } } // function to create Backup schedules (Backup time) function createBackupSchedule() { for (const type in backupConfig) { if (!backupConfig.hasOwnProperty(type)) { continue; } const config = backupConfig[type]; if (config.enabled === true || config.enabled === 'true') { let time = config.ownCron ? config.cronjob : config.time.split(':'); const backupInfo = config.ownCron ? `with Cronjob "${config.cronjob}"` : `at ${config.time} every ${config.everyXDays} day(s)`; adapter.log.info(`[${type}] backup will be activated ${backupInfo}`); if (backupTimeSchedules[type]) { backupTimeSchedules[type].cancel(); } const cron = config.ownCron ? time : `10 ${time[1]} ${time[0]} */${config.everyXDays} * * `; backupTimeSchedules[type] = schedule.scheduleJob(cron, async () => { const sysCheck = await systemCheck.storageSizeCheck(adapter, adapterName, adapter.log); if ((sysCheck && sysCheck.ready && sysCheck.ready === true) || adapter.config.cifsEnabled === true) { adapter.setState(`oneClick.${type}`, true, true); startBackup(backupConfig[type], err => { if (err) { adapter.log.error(`[${type}] ${err}`); } else { adapter.log.debug(`[${type}] exec: done`); } timerOutput2 = setTimeout(() => adapter.getState('output.line', (err, state) => { if (state && state.val === '[EXIT] 0') { adapter.setState(`history.${type}Success`, true, true); adapter.setState(`history.${type}LastTime`, tools.getTimeString(systemLang), true); if (adapter.config.onedriveEnabled && adapter.config.hostType === 'Single') { renewOnedriveToken(); } } else { adapter.setState(`history.${type}LastTime`, `error: ${tools.getTimeString(systemLang)}`, true); adapter.setState(`history.${type}Success`, false, true); } }), 500); nextBackup(false, type); adapter.setState(`oneClick.${type}`, false, true); if (adapter.config.slaveInstance && type === 'iobroker' && adapter.config.hostType === 'Master') { adapter.log.debug('Slave backup from BackItUp-Master is started ...'); startSlaveBackup(adapter.config.slaveInstance[0], null); } }); } else { adapter.log.error(`A local backup is currently not possible. The storage space is currently only ${sysCheck && sysCheck.diskFree ? sysCheck.diskFree : null} MB`); systemCheck.systemMessage(adapter, tools._('A local backup is currently not possible. Please check your System!', systemLang)); } }); if (config.debugging) { adapter.log.debug(`[${type}] ${cron}`); } } else if (backupTimeSchedules[type]) { adapter.log.info(`[${type}] backup deactivated`); backupTimeSchedules[type].cancel(); backupTimeSchedules[type] = null; } } } async function initConfig(secret) { return new Promise(async (resolve) => { // compatibility if (adapter.config.cifsMount === 'CIFS') { adapter.config.cifsMount = ''; } if (adapter.config.redisEnabled === undefined) { adapter.config.redisEnabled = adapter.config.backupRedis } let ioPath; try { // ioPath = `${ioCommon.tools.getControllerDir()}/iobroker.js`; Todo: Error by iob Backup (no such file or directory, uv_cwd) // ioPath = require.resolve('iobroker.js-controller/iobroker.js'); ioPath = path.resolve(__dirname, '../iobroker.js-controller/iobroker.js'); } catch (e) { adapter.log.error(`Unable to read iobroker path: +${e}`); } decryptEvents(secret); const telegram = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.telegramInstance, SilentNotice: adapter.config.telegramSilentNotice, NoticeType: adapter.config.telegramNoticeType, User: adapter.config.telegramUser, onlyError: adapter.config.telegramOnlyError, telegramWaiting: adapter.config.telegramWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const whatsapp = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.whatsappInstance, NoticeType: adapter.config.whatsappNoticeType, onlyError: adapter.config.whatsappOnlyError, whatsappWaiting: adapter.config.whatsappWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const gotify = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.gotifyInstance, NoticeType: adapter.config.gotifyNoticeType, onlyError: adapter.config.gotifyOnlyError, gotifyWaiting: adapter.config.gotifyWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const signal = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.signalInstance, NoticeType: adapter.config.signalNoticeType, onlyError: adapter.config.signalOnlyError, signalWaiting: adapter.config.signalWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const matrix = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.matrixInstance, NoticeType: adapter.config.matrixNoticeType, onlyError: adapter.config.matrixOnlyError, matrixWaiting: adapter.config.matrixWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const discord = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.discordInstance, NoticeType: adapter.config.discordNoticeType, target: adapter.config.discordTarget, onlyError: adapter.config.discordOnlyError, discordWaiting: adapter.config.discordWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const pushover = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.pushoverInstance, SilentNotice: adapter.config.pushoverSilentNotice, NoticeType: adapter.config.pushoverNoticeType, deviceID: adapter.config.pushoverDeviceID, onlyError: adapter.config.pushoverOnlyError, pushoverWaiting: adapter.config.pushoverWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const email = { enabled: adapter.config.notificationEnabled, notificationsType: adapter.config.notificationsType, type: 'message', instance: adapter.config.emailInstance, NoticeType: adapter.config.emailNoticeType, emailReceiver: adapter.config.emailReceiver, emailSender: adapter.config.emailSender, onlyError: adapter.config.emailOnlyError, emailWaiting: adapter.config.emailWaitToSend * 1000, hostName: adapter.config.minimalNameSuffix ? adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_') : '', ignoreErrors: adapter.config.ignoreErrors, systemLang }; const notification = { type: 'message', ignoreErrors: adapter.config.ignoreErrors, bashDir: bashDir, entriesNumber: adapter.config.historyEntriesNumber, systemLang }; const historyHTML = { enabled: true, type: 'message', entriesNumber: adapter.config.historyEntriesNumber, ignoreErrors: adapter.config.ignoreErrors, systemLang }; const historyJSON = { enabled: true, type: 'message', entriesNumber: adapter.config.historyEntriesNumber, ignoreErrors: adapter.config.ignoreErrors, systemLang }; const ftp = { enabled: adapter.config.ftpEnabled, type: 'storage', source: adapter.config.restoreSource, host: adapter.config.ftpHost, // ftp-host debugging: adapter.config.debugLevel, deleteOldBackup: adapter.config.ftpDeleteOldBackup, // Delete old Backups from FTP ftpDeleteAfter: adapter.config.ftpDeleteAfter, advancedDelete: adapter.config.advancedDelete, ownDir: adapter.config.ftpOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.ftpOwnDir === true) ? null : adapter.config.ftpDir, // directory on FTP server dirMinimal: adapter.config.ftpMinimalDir, user: adapter.config.ftpUser, // username for FTP Server pass: adapter.config.ftpPassword || '', // password for FTP Server port: adapter.config.ftpPort || 21, // FTP port secure: adapter.config.ftpSecure || false, // secure FTP connection signedCertificates: adapter.config.ftpSignedCertificates || true, ignoreErrors: adapter.config.ignoreErrors }; let accessToken = ''; if (adapter.config.dropboxEnabled) { dropBoxTokenRefresher = new TokenRefresher(adapter, 'info.dropboxTokens', 'https://oauth2.iobroker.in/dropbox'); try { accessToken = await dropBoxTokenRefresher.getAccessToken(); } catch (e) { adapter.log.error(`No DropBox token found: ${e}`); } } const dropbox = { enabled: adapter.config.dropboxEnabled, type: 'storage', source: adapter.config.restoreSource, debugging: adapter.config.debugLevel, deleteOldBackup: adapter.config.dropboxDeleteOldBackup, // Delete old Backups from Dropbox dropboxDeleteAfter: adapter.config.dropboxDeleteAfter, advancedDelete: adapter.config.advancedDelete, accessToken: adapter.config.dropboxTokenType === 'custom' ? adapter.config.dropboxAccessToken : accessToken, dropboxAccessJson: adapter.config.dropboxAccessJson, dropboxTokenType: adapter.config.dropboxTokenType, ownDir: adapter.config.dropboxOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.dropboxOwnDir === true) ? null : adapter.config.dropboxDir, dirMinimal: adapter.config.dropboxMinimalDir, ignoreErrors: adapter.config.ignoreErrors, }; const onedrive = { enabled: adapter.config.onedriveEnabled, type: 'storage', source: adapter.config.restoreSource, debugging: adapter.config.debugLevel, deleteOldBackup: adapter.config.onedriveDeleteOldBackup, // Delete old Backups from Onedrive onedriveDeleteAfter: adapter.config.onedriveDeleteAfter, advancedDelete: adapter.config.advancedDelete, onedriveAccessJson: adapter.config.onedriveAccessJson, ownDir: adapter.config.onedriveOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.onedriveOwnDir === true) ? null : adapter.config.onedriveDir, dirMinimal: adapter.config.onedriveMinimalDir, ignoreErrors: adapter.config.ignoreErrors }; const webdav = { enabled: adapter.config.webdavEnabled, type: 'storage', source: adapter.config.restoreSource, debugging: adapter.config.debugLevel, deleteOldBackup: adapter.config.webdavDeleteOldBackup, // Delete old Backups from webdav webdavDeleteAfter: adapter.config.webdavDeleteAfter, advancedDelete: adapter.config.advancedDelete, username: adapter.config.webdavUsername, pass: adapter.config.webdavPassword || '', // webdav password url: adapter.config.webdavURL, ownDir: adapter.config.webdavOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.webdavOwnDir === true) ? null : adapter.config.webdavDir, dirMinimal: adapter.config.webdavMinimalDir, signedCertificates: adapter.config.webdavSignedCertificates, ignoreErrors: adapter.config.ignoreErrors }; const googledrive = { enabled: adapter.config.googledriveEnabled, type: 'storage', source: adapter.config.restoreSource, debugging: adapter.config.debugLevel, deleteOldBackup: adapter.config.googledriveDeleteOldBackup, // Delete old Backups from google drive googledriveDeleteAfter: adapter.config.googledriveDeleteAfter, advancedDelete: adapter.config.advancedDelete, accessJson: adapter.config.googledriveAccessTokens || adapter.config.googledriveAccessJson, newToken: !!adapter.config.googledriveAccessTokens, ownDir: adapter.config.googledriveOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.googledriveOwnDir === true) ? null : adapter.config.googledriveDir, dirMinimal: adapter.config.googledriveMinimalDir, ignoreErrors: adapter.config.ignoreErrors }; const cifs = { enabled: adapter.config.cifsEnabled, mountType: adapter.config.connectType, type: 'storage', source: adapter.config.restoreSource, mount: adapter.config.cifsMount, debugging: adapter.config.debugLevel, fileDir: bashDir, wakeOnLAN: adapter.config.wakeOnLAN, macAd: adapter.config.macAd, wolTime: adapter.config.wolWait, wolPort: adapter.config.wolPort || 9, wolExtra: adapter.config.wolExtra, smb: adapter.config.smbType, sudo: adapter.config.sudoMount, cifsDomain: adapter.config.cifsDomain, clientInodes: adapter.config.noserverino, cacheLoose: adapter.config.cacheLoose, deleteOldBackup: adapter.config.cifsDeleteOldBackup, //Delete old Backups from Network Disk ownDir: adapter.config.cifsOwnDir, bkpType: adapter.config.restoreType, dir: (adapter.config.cifsOwnDir === true) ? null : adapter.config.cifsDir, // specify if CIFS mount should be used dirMinimal: adapter.config.cifsMinimalDir, user: adapter.config.cifsUser, // specify if CIFS mount should be used pass: adapter.config.cifsPassword || '', // password for NAS Server expertMount: adapter.config.expertMount, ignoreErrors: adapter.config.ignoreErrors }; // Configurations for standard-IoBroker backup backupConfig.iobroker = { name: 'iobroker', type: 'creator', workDir: ioPath, enabled: adapter.config.minimalEnabled, time: adapter.config.minimalTime, cronjob: adapter.config.iobrokerCronJob, ownCron: adapter.config.iobrokerCron, debugging: adapter.config.debugLevel, slaveBackup: adapter.config.hostType, everyXDays: adapter.config.minimalEveryXDays, nameSuffix: adapter.config.minimalNameSuffix.replace(/[.;, ]/g, '_'), // names addition, appended to the file name deleteBackupAfter: adapter.config.minimalDeleteAfter, // delete old backup files after x days ftp: Object.assign({}, ftp, (adapter.config.ftpOwnDir === true) ? { dir: adapter.config.ftpMinimalDir } : {}), cifs: Object.assign({}, cifs, (adapter.config.cifsOwnDir === true) ? { dir: adapter.config.cifsMinimalDir } : {}), dropbox: Object.assign({}, dropbox, (adapter.config.dropboxOwnDir === true) ? { dir: adapter.config.dropboxMinimalDir } : {}), onedrive: Object.assign({}, onedrive, (adapter.config.onedriveOwnDir === true) ? { dir: adapter.config.onedriveMinimalDir } : {}), webdav: Object.assign({}, webdav, (adapter.config.webdavOwnDir === true) ? { dir: adapter.config.webdavMinimalDir } : {}), googledrive: Object.assign({}, googledrive, (adapter.config.googledriveOwnDir === true) ? { dir: adapter.config.googledriveMinimalDir } : {}), ignoreErrors: adapter.config.ignoreErrors, mysql: { enabled: adapter.config.mySqlEnabled === undefined ? true : adapter.config.mySqlEnabled, type: 'creator', ftp: Object.assign({}, ftp, (adapter.config.ftpOwnDir === true) ? { dir: adapter.config.ftpMinimalDir } : {}), cifs: Object.assign({}, cifs, (adapter.config.cifsOwnDir === true) ? { dir: adapter.config.cifsMinimalDir } : {}), dropbox: Object.assign({}, dropbox, (adapter.config.dropboxOwnDir === true) ? { dir: adapter.config.dropboxMinimalDir } : {}), onedrive: Object.assign({}, onedrive, (adapter.config.onedriveOwnDir === true) ? { dir: adapter.config.onedriveMinimalDir } : {}), webdav: Object.assign({}, webdav, (adapter.config.webdavOwnDir === true) ? { dir: adapter.config.webdavMi