UNPKG

iobroker.js-controller

Version:

Updated by reinstall.js on 2018-06-11T15:19:56.688Z

440 lines (394 loc) • 17.6 kB
'use strict'; /** @class */ function Repo(options) { const { EXIT_CODES } = require('@iobroker/js-controller-common'); const { tools } = require('@iobroker/js-controller-common'); const axios = require('axios'); const ioPackage = require('../../io-package.json'); const version = ioPackage.common.version; const fs = require('fs'); const defaultSystemRepo = { common: { name: 'System repositories', dontDelete: true }, native: { repositories: { stable: { link: 'http://download.iobroker.net/sources-dist.json', json: null }, beta: { link: 'http://download.iobroker.net/sources-dist-latest.json', json: null } } }, _id: 'system.repositories', type: 'config' }; options = options || {}; if (!options.objects) { throw new Error('Invalid arguments: objects is missing'); } if (!options.states) { throw new Error('Invalid arguments: states is missing'); } const objects = options.objects; const states = options.states; async function updateRepo(repoName, force, _systemConfig, _systemRepos) { if (!repoName) { const sysConfig = _systemConfig || (await objects.getObjectAsync('system.config')); repoName = sysConfig.common.activeRepo; } const oldRepos = _systemRepos || (await objects.getObjectAsync('system.repositories')); if (!oldRepos.native.repositories || !oldRepos.native.repositories[repoName]) { console.log(`Error: repository "${repoName}" not found in the "system.repositories`); return null; } const urlOrPath = oldRepos.native.repositories[repoName].link; const hashUrl = urlOrPath.replace(/\.json$/, '-hash.json'); let hash; if ( !force && oldRepos.native.repositories[repoName].hash && oldRepos.native.repositories[repoName].json && (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) ) { try { hash = await axios({ url: hashUrl, timeout: 10000 }); } catch (e) { console.error(`Cannot download repository hash file from "${hashUrl}": ${e.message}`); } if (hash && hash.data && oldRepos.native.repositories[repoName].hash === hash.data.hash) { return oldRepos.native.repositories[repoName].json; } } let data; if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) { if (!hash) { try { hash = await axios({ url: hashUrl, timeout: 10000 }); } catch (e) { console.error(`Cannot download repository hash file from "${hashUrl}": ${e.message}`); } } const agent = `${tools.appName}, RND: CLI, Node:${process.version}, V:${version}`; try { data = await axios({ url: urlOrPath, timeout: 10000, headers: { 'User-Agent': agent } }); if (data.data) { data = data.data; } else { data = null; } } catch (e) { console.error(`Cannot download repository file from "${urlOrPath}": ${e.message}`); data = null; } } else { if (fs.existsSync(urlOrPath)) { try { data = JSON.parse(fs.readFileSync(urlOrPath).toString('utf8')); } catch (err) { console.error(`Error: Cannot read or parse file "${urlOrPath}": ${err.message}`); } } else { console.error(`Error: Cannot find file "${urlOrPath}"`); } } let changed; if (data) { oldRepos.native.repositories[repoName].json = data; changed = true; } if (hash && hash.data) { oldRepos.native.repositories[repoName].hash = hash.data.hash; changed = true; } if (changed) { oldRepos.from = `system.host.${tools.getHostName()}.cli`; oldRepos.ts = Date.now(); await objects.setObjectAsync('system.repositories', oldRepos); } return oldRepos.native.repositories[repoName].json; } this.showRepo = async function (repoUrl, flags) { function showRepoResult(_name, sources) { const installed = tools.getInstalledInfo(); const adapters = Object.keys(sources).sort(); adapters.forEach(name => { let updatable = false; let text = sources[name].controller ? 'Controller ' : 'Adapter '; text += `"${name}"`; text = text.padEnd(11 + 15); if (sources[name].version) { text += ': ' + sources[name].version; } text = text.padEnd(11 + 15 + 11); if (!(flags.all || flags.a) && !installed[name]) { return; } if (installed[name] && installed[name].version) { text += ', installed ' + installed[name].version; try { // tools.upToDate can throw if version is invalid if ( sources[name].version !== installed[name].version && sources[name].version && !tools.upToDate(sources[name].version, installed[name].version) ) { updatable = true; text = text.padEnd(11 + 15 + 11 + 18); text += ' [Updatable]'; } } catch (e) { console.error(`Cannot determine update info of "${name}": ${e.message}`); } } if ((flags.updatable || flags.u) && !updatable) { return; } console.log(text); }); } // Get the repositories const systemConfig = await objects.getObjectAsync('system.config'); const systemRepos = await objects.getObjectAsync('system.repositories'); if (!systemConfig) { console.error('Error: Object "system.config" not found'); } else if (!systemRepos) { console.error('Error: Object "system.repositories" not found'); } else if (!systemRepos.native || !systemRepos.native.repositories) { console.error('Error: no repositories found in the "system.config'); } else { repoUrl = repoUrl || systemConfig.common.activeRepo; if (!Array.isArray(repoUrl)) { repoUrl = [repoUrl]; } console.log(`Used ${repoUrl.length > 1 ? 'repositories' : 'repository'}: ${repoUrl.join(', ')}`); const allSources = {}; for (let r = 0; r < repoUrl.length; r++) { const repo = repoUrl[r]; // If known repository if (systemRepos.native.repositories[repo]) { if (typeof systemRepos.native.repositories[repo] === 'string') { systemRepos.native.repositories[repo] = { link: systemRepos.native.repositories[repo], json: null, hash: '' }; } const sources = await updateRepo(repo, flags.force || flags.f, systemConfig, systemRepos); sources && Object.assign(allSources, sources); } else { console.error( `Error: unknown repository is active - "${repo}". Known: ${Object.keys( systemRepos.native.repositories ).join(', ')}` ); } } try { // update variables of every admin instance await updateInfo(allSources); } catch { // not important if fails } showRepoResult(null, allSources); } }; async function updateInfo(sources) { const installed = tools.getInstalledInfo(); const list = []; Object.keys(sources).forEach(name => { if (installed[name] && installed[name].version && sources[name].version) { try { // tools.upToDate can throw if version is invalid if ( sources[name].version !== installed[name].version && !tools.upToDate(sources[name].version, installed[name].version) ) { // remove first part of the name const n = name.indexOf('.'); list.push(n === -1 ? name : name.substring(n + 1)); } } catch (e) { console.error(`Cannot determine update info of "${name}": ${e.message}`); } } }); const objs = await objects.getObjectViewAsync('system', 'instance', { startkey: 'system.adapter.admin', endkey: 'system.adapter.admin\u9999' }); if (objs && objs.rows && objs.rows.length) { const listStr = list.join(', '); for (let t = 0; t < objs.rows.length; t++) { if ( objs.rows[t] && objs.rows[t].value && objs.rows[t].value.common && objs.rows[t].value.type === 'instance' ) { await states.setStateAsync(objs.rows[t].id + '.info.updatesNumber', list.length, true); await states.setStateAsync(objs.rows[t].id + '.info.updatesList', listStr, true); } } } } this.showRepoStatus = async function () { try { const obj = await objects.getObjectAsync('system.repositories'); if (!obj) { console.error('List is empty'); return EXIT_CODES.CANNOT_GET_REPO_LIST; } else if (obj.native.repositories) { Object.keys(obj.native.repositories).forEach(r => console.log(`${r.padEnd(14)}: ${obj.native.repositories[r].link}`) ); const objCfg = await objects.getObjectAsync('system.config'); if (objCfg && objCfg.common) { let activeRepo = objCfg.common.activeRepo; if (typeof activeRepo === 'string') { activeRepo = [activeRepo]; } console.log(`\nActive repo(s): ${activeRepo.join(', ')}`); } } else { console.error('List is empty'); return EXIT_CODES.CANNOT_GET_REPO_LIST; } } catch (err) { console.error('Cannot get list: ' + err); return EXIT_CODES.CANNOT_GET_REPO_LIST; } }; this.add = async function (repoName, repoUrl) { let obj = await objects.getObjectAsync('system.repositories'); obj = obj || defaultSystemRepo; if (obj.native.repositories[repoName]) { throw new Error(`Repository "${repoName}" yet exists: ${obj.native.repositories[repoName].link}`); } else { obj.native.repositories[repoName] = { link: repoUrl, json: null }; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = Date.now(); await objects.setObjectAsync('system.repositories', obj); } }; this.del = async function (repoName) { const obj = await objects.getObjectAsync('system.config'); if ( (obj.common.activeRepo && typeof obj.common.activeRepo === 'string' && obj.common.activeRepo === repoName) || (obj.common.activeRepo && Array.isArray(obj.common.activeRepo) && obj.common.activeRepo.includes(repoName)) ) { throw new Error(`Cannot delete active repository: ${repoName}`); } else { const repoObj = await objects.getObjectAsync('system.repositories'); if (repoObj) { if (!repoObj.native.repositories[repoName]) { throw new Error(`Repository "${repoName}" not found.`); } else { delete repoObj.native.repositories[repoName]; repoObj.from = `system.host.${tools.getHostName()}.cli`; repoObj.ts = Date.now(); await objects.setObjectAsync('system.repositories', repoObj); } } } }; this.setActive = async function (repoName) { let obj = await objects.getObjectAsync('system.repositories'); obj = obj || defaultSystemRepo; if (!obj.native.repositories[repoName]) { throw new Error(`Repository "${repoName}" not found.`); } else { const confObj = await objects.getObjectAsync('system.config'); if (typeof confObj.common.activeRepo === 'string') { confObj.common.activeRepo = [confObj.common.activeRepo]; } if (!confObj.common.activeRepo.includes(repoName)) { confObj.common.activeRepo.push(repoName); confObj.from = `system.host.${tools.getHostName()}.cli`; confObj.ts = Date.now(); await objects.setObjectAsync('system.config', confObj); } } }; this.setInactive = async function (repoName) { const confObj = await objects.getObjectAsync('system.config'); if (typeof confObj.common.activeRepo === 'string') { confObj.common.activeRepo = [confObj.common.activeRepo]; } const pos = confObj.common.activeRepo.indexOf(repoName); if (pos !== -1) { confObj.common.activeRepo.splice(pos, 1); confObj.from = `system.host.${tools.getHostName()}.cli`; confObj.ts = Date.now(); await objects.setObjectAsync('system.config', confObj); } }; /** * Renames existing repository if old name and link matches, renaming will not be performed if an repo with the new name already exists * * @param {string} oldName - name of the current repository * @param {string} newName - target name * @param {string} matchingLink - hyperlink of the repository * @returns {Promise<void>} */ this.rename = async function (oldName, newName, matchingLink) { let repoObj; let sysConfigObj; try { sysConfigObj = await objects.getObjectAsync('system.config'); repoObj = await objects.getObjectAsync('system.repositories'); } catch (err) { throw new Error(`Could not rename repository "${oldName}" to "${newName}": ${err.message}`); } if (repoObj && repoObj.native && repoObj.native.repositories) { if ( repoObj.native.repositories[oldName] && repoObj.native.repositories[oldName].link === matchingLink && !repoObj.native.repositories[newName] ) { repoObj.native.repositories[newName] = repoObj.native.repositories[oldName]; delete repoObj.native.repositories[oldName]; try { await objects.setObjectAsync('system.repositories', repoObj); console.log(`Renamed repository "${oldName} to "${newName}"`); } catch (err) { throw new Error(`Could not rename repository "${oldName}" to "${newName}": ${err.message}`); } // if we changed the name of the activeRepo, we should set newName as active repo if ( sysConfigObj && sysConfigObj.common && ((typeof sysConfigObj.common.activeRepo === 'string' && sysConfigObj.common.activeRepo === oldName) || (Array.isArray(sysConfigObj.common.activeRepo) && sysConfigObj.common.activeRepo.includes(oldName))) ) { if (typeof sysConfigObj.common.activeRepo === 'string') { sysConfigObj.common.activeRepo = [sysConfigObj.common.activeRepo]; } const pos = sysConfigObj.common.activeRepo.indexOf(oldName); sysConfigObj.common.activeRepo.splice(pos, 1, newName); try { await objects.setObjectAsync('system.config', sysConfigObj); } catch (err) { throw new Error(`Could not set "${newName}" as active repository: ${err.message}`); } } } } }; } module.exports = Repo;