yunkong2.admin/main.js
2018-09-14 19:26:46 +08:00

364 lines
12 KiB
JavaScript

/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
const adapterName = require(__dirname + '/package.json').name.split('.').pop();
const utils = require(__dirname + '/lib/utils'); // Get common adapter utils
const tools = require(utils.controllerDir + '/lib/tools.js');
const SocketIO = require(__dirname + '/lib/socket');
const Web = require(__dirname + '/lib/web');
let socket = null;
let webServer = null;
let objects = {};
let states = {};
let secret = 'Zgfr56gFe87jJOM'; // Will be generated by first start
let adapter = new utils.Adapter({
name: adapterName, // adapter name
dirname: __dirname, // say own position
logTransporter: true, // receive the logs
systemConfig: true,
install: callback => typeof callback === 'function' && callback()
});
adapter.on('objectChange', (id, obj) => {
if (obj) {
//console.log('objectChange: ' + id);
objects[id] = obj;
if (id === 'system.repositories') {
writeUpdateInfo();
}
} else {
//console.log('objectDeleted: ' + id);
if (objects[id]) {
delete objects[id];
}
}
// TODO Build in some threshold of messages
if (socket) {
socket.objectChange(id, obj);
}
});
adapter.on('stateChange', (id, state) => {
if (!state) {
if (states[id]) {
delete states[id];
}
} else {
states[id] = state;
}
if (socket) {
socket.stateChange(id, state);
}
});
adapter.on('ready', () => {
adapter.getForeignObject('system.config', (err, obj) => {
if (!err && obj) {
obj.native = obj.native || {};
if (!obj.native.secret) {
require('crypto').randomBytes(24, (ex, buf) => {
adapter.config.secret = buf.toString('hex');
adapter.extendForeignObject('system.config', {native: {secret: adapter.config.secret}});
main();
});
} else {
adapter.config.secret = obj.native.secret;
main();
}
} else {
adapter.config.secret = secret;
adapter.logger.error('Cannot find object system.config');
}
});
});
adapter.on('message', obj => {
if (!obj || !obj.message) {
return false;
}
if (socket) {
socket.sendCommand(obj);
}
return true;
});
adapter.on('unload', callback => {
if (socket) {
// unsubscribe all
socket.unsubscribeAll();
}
try {
adapter.log.info('terminating http' + (adapter.config.secure ? 's' : '') + ' server on port ' + adapter.config.port);
webServer.close();
callback();
} catch (e) {
callback();
}
});
// obj = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
adapter.on('log', obj => socket && socket.sendLog(obj));
function createUpdateInfo() {
// create connected object and state
let obj = objects[adapter.namespace + '.info.updatesNumber'];
if (!obj || !obj.common || obj.common.type !== 'number') {
obj = {
_id: 'info.updatesNumber',
type: 'state',
common: {
role: 'indicator.updates',
name: 'Number of adapters to update',
type: 'number',
read: true,
write: false,
def: 0
},
native: {}
};
adapter.setObject(obj._id, obj);
}
obj = objects[adapter.namespace + '.info.updatesList'];
if (!obj || !obj.common || obj.common.type !== 'string') {
obj = {
_id: 'info.updatesList',
type: 'state',
common: {
role: 'indicator.updates',
name: 'List of adapters to update',
type: 'string',
read: true,
write: false,
def: ''
},
native: {}
};
adapter.setObject(obj._id, obj);
}
}
// Helper methods
function upToDate(a, b) {
a = a.split('.');
b = b.split('.');
a[0] = parseInt(a[0], 10);
b[0] = parseInt(b[0], 10);
if (a[0] > b[0]) {
return false;
} else if (a[0] < b[0]) {
return true;
} else if (a[0] === b[0]) {
a[1] = parseInt(a[1], 10);
b[1] = parseInt(b[1], 10);
if (a[1] > b[1]) {
return false;
} else if (a[1] < b[1]) {
return true;
} else if (a[1] === b[1]) {
a[2] = parseInt(a[2], 10);
b[2] = parseInt(b[2], 10);
return a[2] <= b[2];
}
} else {
return true;
}
}
function writeUpdateInfo(sources) {
if (!sources) {
let obj = objects['system.repositories'];
if (!objects['system.config'] || !objects['system.config'].common) {
adapter.log.warn('Repository cannot be read. Invalid "system.config" object.');
return;
}
const activeRepo = objects['system.config'].common.activeRepo;
if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo] &&
obj.native.repositories[activeRepo].json) {
sources = obj.native.repositories[activeRepo].json;
} else {
adapter.setState('info.updatesNumber', 0, true);
adapter.setState('info.updatesList', '', true);
if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo]) {
adapter.log.warn('Repository cannot be read');
} else {
adapter.log.warn('No repository source configured');
}
return;
}
}
let installed = tools.getInstalledInfo();
let list = [];
for (let name in sources) {
if (!sources.hasOwnProperty(name)) continue;
if (installed[name] && installed[name].version && sources[name].version) {
if (sources[name].version !== installed[name].version &&
!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));
}
}
}
adapter.setState('info.updatesNumber', list.length, true);
adapter.setState('info.updatesList', list.join(', '), true);
}
// to do => remove it later, when all repositories patched.
function patchRepos(callback) {
return callback && callback();
// do not patch any more. Delete it later 2018.04.23
/*
adapter.getForeignObject('system.repositories', (err, obj) => {
let changed = false;
if (obj && obj.native && obj.native.repositories) {
// default link should point to stable
if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.yunkong2.net/sources-dist.json') {
changed = true;
obj.native.repositories.default = {
link: 'http://download.yunkong2.net/sources-dist.json'
};
}
// latest link should point to latest
if (!obj.native.repositories.latest) {
obj.native.repositories.latest = {
link: 'http://download.yunkong2.net/sources-dist-latest.json'
};
changed = true;
}
// change URL of raw sources from yunkong2.js-controller to yunkong2.repositories
for (let r in obj.native.repositories) {
if (obj.native.repositories.hasOwnProperty(r) &&
obj.native.repositories[r].link === 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/conf/sources-dist.json') {
obj.native.repositories[r].link = 'https://raw.githubusercontent.com/yunkong2/yunkong2.repositories/master/sources-dist.json';
changed = true;
}
}
}
if (changed) {
adapter.setForeignObject(obj._id, obj, function () {
callback && callback();
});
} else {
callback && callback();
}
});*/
}
function initSocket(server, store) {
socket = new SocketIO(server, adapter.config, adapter, objects, states, store);
socket.subscribe(null, 'objectChange', '*');
}
function main() {
// adapter.subscribeForeignStates('*');
// adapter.subscribeForeignObjects('*');
adapter.config.defaultUser = adapter.config.defaultUser || 'admin';
if (!adapter.config.defaultUser.match(/^system\.user\./)) {
adapter.config.defaultUser = 'system.user.' + adapter.config.defaultUser;
}
if (adapter.config.secure) {
// Load certificates
adapter.getCertificates((err, certificates, leConfig) => {
adapter.config.certificates = certificates;
adapter.config.leConfig = leConfig;
getData(() => webServer = new Web(adapter.config, adapter, initSocket));
});
} else {
getData(() => webServer = new Web(adapter.config, adapter, initSocket));
}
patchRepos(() => {
// By default update repository every 24 hours
if (adapter.config.autoUpdate === undefined) {
adapter.config.autoUpdate = 24;
}
adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
if (adapter.config.autoUpdate) {
setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000);
updateRegister();
}
});
}
function getData(callback) {
adapter.log.info('requesting all states');
let tasks = 0;
tasks++;
adapter.getForeignStates('*', (err, res) => {
adapter.log.info('received all states');
states = res;
if (!--tasks && callback) callback();
});
adapter.log.info('requesting all objects');
tasks++;
adapter.objects.getObjectList({include_docs: true}, (err, res) => {
adapter.log.info('received all objects');
res = res.rows;
objects = {};
let tmpPath = '';
for (let i = 0; i < res.length; i++) {
objects[res[i].doc._id] = res[i].doc;
if (res[i].doc.type === 'instance' && res[i].doc.common && res[i].doc.common.tmpPath) {
if (tmpPath) {
adapter.log.warn('tmpPath has multiple definitions!!');
}
tmpPath = res[i].doc.common.tmpPath;
}
}
// Some adapters want access on specified tmp directory
if (tmpPath) {
adapter.config.tmpPath = tmpPath;
adapter.config.tmpPathAllow = true;
}
createUpdateInfo();
writeUpdateInfo();
if (!--tasks && callback) callback();
});
}
// read repository information from active repository
function updateRegister() {
adapter.log.info('Request actual repository...');
adapter.getForeignObject('system.config', (err, data) => {
if (data && data.common) {
adapter.sendToHost(adapter.host, 'getRepository', {
repo: data.common.activeRepo,
update: true
}, _repository => {
if (_repository === 'permissionError') {
adapter.log.error('May not read "getRepository"');
} else {
adapter.log.info('Repository received successfully.');
if (socket) {
socket.repoUpdated();
}
}
});
}
});
}