More work on integrating the script-task.
This commit is contained in:
parent
3aca17ea6d
commit
1b67b84369
|
@ -238,6 +238,7 @@
|
|||
<Compile Include="public\scripts\common-0.0.1.js" />
|
||||
<Compile Include="public\scripts\meshcentral.js" />
|
||||
<Compile Include="redirserver.js" />
|
||||
<Compile Include="taskmanager.js" />
|
||||
<Compile Include="translate\translate.js" />
|
||||
<Compile Include="ua-parser.js" />
|
||||
<Compile Include="webauthn.js" />
|
||||
|
|
15
meshagent.js
15
meshagent.js
|
@ -1740,20 +1740,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
}
|
||||
case 'script-task': {
|
||||
// These command are for running regular batch jobs on the remote device
|
||||
switch (command.subaction) {
|
||||
case 'getScript': {
|
||||
console.log('getScript');
|
||||
break;
|
||||
}
|
||||
case 'clearAllPendingTasks': {
|
||||
console.log('clearAllPendingTasks');
|
||||
break;
|
||||
}
|
||||
case 'taskComplete': {
|
||||
console.log('taskComplete');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parent.parent.taskManager != null) { parent.parent.taskManager.agentAction(command, obj); }
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
|
@ -33,6 +33,7 @@ function CreateMeshCentralServer(config, args) {
|
|||
obj.amtScanner = null;
|
||||
obj.amtManager = null;
|
||||
obj.meshScanner = null;
|
||||
obj.taskManager = null;
|
||||
obj.letsencrypt = null;
|
||||
obj.eventsDispatch = {};
|
||||
obj.fs = require('fs');
|
||||
|
@ -1497,6 +1498,11 @@ function CreateMeshCentralServer(config, args) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Setup the task manager
|
||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.taskmanager == true)) {
|
||||
obj.taskManager = require('./taskmanager').createTaskManager(obj);
|
||||
}
|
||||
|
||||
// Start plugin manager if configuration allows this.
|
||||
if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null) && (obj.config.settings.plugins != false) && ((typeof obj.config.settings.plugins != 'object') || (obj.config.settings.plugins.enabled != false))) {
|
||||
obj.pluginHandler = require('./pluginHandler.js').pluginHandler(obj);
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,714 @@
|
|||
/**
|
||||
* @description MeshCentral ScriptTask
|
||||
* @author Ryan Blenis
|
||||
* @copyright
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports.createTaskManager = function (parent) {
|
||||
var obj = {};
|
||||
obj.parent = parent.webserver;
|
||||
obj.meshServer = parent;
|
||||
obj.db = null;
|
||||
obj.intervalTimer = null;
|
||||
obj.debug = obj.meshServer.debug;
|
||||
obj.VIEWS = __dirname + '/views/';
|
||||
obj.exports = [
|
||||
'onDeviceRefreshEnd',
|
||||
'resizeContent',
|
||||
'historyData',
|
||||
'variableData',
|
||||
'malix_triggerOption'
|
||||
];
|
||||
|
||||
obj.malix_triggerOption = function(selectElem) {
|
||||
selectElem.options.add(new Option("ScriptTask - Run Script", "scripttask_runscript"));
|
||||
}
|
||||
obj.malix_triggerFields_scripttask_runscript = function() {
|
||||
|
||||
}
|
||||
obj.resetQueueTimer = function() {
|
||||
clearTimeout(obj.intervalTimer);
|
||||
obj.intervalTimer = setInterval(obj.queueRun, 1 * 60 * 1000); // every minute
|
||||
};
|
||||
|
||||
// Start the task manager
|
||||
obj.server_startup = function() {
|
||||
obj.meshServer.pluginHandler.scripttask_db = require (__dirname + '/db.js').CreateDB(obj.meshServer);
|
||||
obj.db = obj.meshServer.pluginHandler.scripttask_db;
|
||||
obj.resetQueueTimer();
|
||||
};
|
||||
|
||||
obj.onDeviceRefreshEnd = function() {
|
||||
pluginHandler.registerPluginTab({ tabTitle: 'ScriptTask', tabId: 'pluginScriptTask' });
|
||||
QA('pluginScriptTask', '<iframe id="pluginIframeScriptTask" style="width: 100%; height: 800px;" scrolling="no" frameBorder=0 src="/pluginadmin.ashx?pin=scripttask&user=1" />');
|
||||
};
|
||||
|
||||
/*
|
||||
// may not be needed, saving for later. Can be called to resize iFrame
|
||||
obj.resizeContent = function() {
|
||||
var iFrame = document.getElementById('pluginIframeScriptTask');
|
||||
var newHeight = 800;
|
||||
var sHeight = iFrame.contentWindow.document.body.scrollHeight;
|
||||
if (sHeight > newHeight) newHeight = sHeight;
|
||||
if (newHeight > 1600) newHeight = 1600;
|
||||
iFrame.style.height = newHeight + 'px';
|
||||
};
|
||||
*/
|
||||
|
||||
obj.queueRun = async function() {
|
||||
var onlineAgents = Object.keys(obj.meshServer.webserver.wsagents);
|
||||
//obj.debug('ScriptTask', 'Queue Running', Date().toLocaleString(), 'Online agents: ', onlineAgents);
|
||||
|
||||
obj.db.getPendingJobs(onlineAgents)
|
||||
.then(function(jobs) {
|
||||
if (jobs.length == 0) return;
|
||||
//@TODO check for a large number and use taskLimiter to queue the jobs
|
||||
jobs.forEach(function(job) {
|
||||
obj.db.get(job.scriptId)
|
||||
.then(async function(script) {
|
||||
script = script[0];
|
||||
var foundVars = script.content.match(/#(.*?)#/g);
|
||||
var replaceVars = {};
|
||||
if (foundVars != null && foundVars.length > 0) {
|
||||
var foundVarNames = [];
|
||||
foundVars.forEach(function(fv) { foundVarNames.push(fv.replace(/^#+|#+$/g, '')); });
|
||||
|
||||
var limiters = {
|
||||
scriptId: job.scriptId,
|
||||
nodeId: job.node,
|
||||
meshId: obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'],
|
||||
names: foundVarNames
|
||||
};
|
||||
var finvals = await obj.db.getVariables(limiters);
|
||||
var ordering = { 'global': 0, 'script': 1, 'mesh': 2, 'node': 3 }
|
||||
finvals.sort(function(a, b) { return (ordering[a.scope] - ordering[b.scope]) || a.name.localeCompare(b.name); });
|
||||
finvals.forEach(function(fv) { replaceVars[fv.name] = fv.value; });
|
||||
replaceVars['GBL:meshId'] = obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'];
|
||||
replaceVars['GBL:nodeId'] = job.node;
|
||||
//console.log('FV IS', finvals);
|
||||
//console.log('RV IS', replaceVars);
|
||||
}
|
||||
var dispatchTime = Math.floor(new Date() / 1000);
|
||||
var jObj = {
|
||||
action: 'task',
|
||||
subaction: 'triggerJob',
|
||||
jobId: job._id,
|
||||
scriptId: job.scriptId,
|
||||
replaceVars: replaceVars,
|
||||
scriptHash: script.contentHash,
|
||||
dispatchTime: dispatchTime
|
||||
};
|
||||
//obj.debug('ScriptTask', 'Sending job to agent');
|
||||
try {
|
||||
obj.meshServer.webserver.wsagents[job.node].send(JSON.stringify(jObj));
|
||||
obj.db.update(job._id, { dispatchTime: dispatchTime });
|
||||
} catch (ex) { }
|
||||
})
|
||||
.catch(function (ex) { console.log('task: Could not dispatch job.', ex) });
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
obj.makeJobsFromSchedules();
|
||||
obj.cleanHistory();
|
||||
})
|
||||
.catch(function(ex) { console.log('task: Queue Run Error: ', ex); });
|
||||
};
|
||||
|
||||
obj.cleanHistory = function() {
|
||||
if (Math.round(Math.random() * 100) == 99) {
|
||||
//obj.debug('Task', 'Running history cleanup');
|
||||
obj.db.deleteOldHistory();
|
||||
}
|
||||
};
|
||||
|
||||
obj.downloadFile = function(req, res, user) {
|
||||
var id = req.query.dl;
|
||||
obj.db.get(id)
|
||||
.then(function(found) {
|
||||
if (found.length != 1) { res.sendStatus(401); return; }
|
||||
var file = found[0];
|
||||
res.setHeader('Content-disposition', 'attachment; filename=' + file.name);
|
||||
res.setHeader('Content-type', 'text/plain');
|
||||
//var fs = require('fs');
|
||||
res.send(file.content);
|
||||
});
|
||||
};
|
||||
|
||||
obj.updateFrontEnd = async function(ids){
|
||||
if (ids.scriptId != null) {
|
||||
var scriptHistory = null;
|
||||
obj.db.getJobScriptHistory(ids.scriptId)
|
||||
.then(function(sh) {
|
||||
scriptHistory = sh;
|
||||
return obj.db.getJobSchedulesForScript(ids.scriptId);
|
||||
})
|
||||
.then(function(scriptSchedule) {
|
||||
var targets = ['*', 'server-users'];
|
||||
obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: ids.scriptId, nodeId: null, scriptHistory: scriptHistory, nodeHistory: null, scriptSchedule: scriptSchedule });
|
||||
});
|
||||
}
|
||||
if (ids.nodeId != null) {
|
||||
var nodeHistory = null;
|
||||
obj.db.getJobNodeHistory(ids.nodeId)
|
||||
.then(function(nh) {
|
||||
nodeHistory = nh;
|
||||
return obj.db.getJobSchedulesForNode(ids.nodeId);
|
||||
})
|
||||
.then(function(nodeSchedule) {
|
||||
var targets = ['*', 'server-users'];
|
||||
obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: null, nodeId: ids.nodeId, scriptHistory: null, nodeHistory: nodeHistory, nodeSchedule: nodeSchedule });
|
||||
});
|
||||
}
|
||||
if (ids.tree === true) {
|
||||
obj.db.getScriptTree()
|
||||
.then(function(tree) {
|
||||
var targets = ['*', 'server-users'];
|
||||
obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'newScriptTree', tree: tree });
|
||||
});
|
||||
}
|
||||
if (ids.variables === true) {
|
||||
obj.db.getVariables()
|
||||
.then(function(vars) {
|
||||
var targets = ['*', 'server-users'];
|
||||
obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'variableData', vars: vars });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
obj.handleAdminReq = function(req, res, user) {
|
||||
if ((user.siteadmin & 0xFFFFFFFF) == 1 && req.query.admin == 1)
|
||||
{
|
||||
// admin wants admin, grant
|
||||
var vars = {};
|
||||
res.render(obj.VIEWS + 'admin', vars);
|
||||
return;
|
||||
} else if (req.query.admin == 1 && (user.siteadmin & 0xFFFFFFFF) == 0) {
|
||||
// regular user wants admin
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
} else if (req.query.user == 1) {
|
||||
// regular user wants regular access, grant
|
||||
if (req.query.dl != null) return obj.downloadFile(req, res, user);
|
||||
var vars = {};
|
||||
|
||||
if (req.query.edit == 1) { // edit script
|
||||
if (req.query.id == null) return res.sendStatus(401);
|
||||
obj.db.get(req.query.id)
|
||||
.then(function(scripts) {
|
||||
if (scripts[0].filetype == 'proc') {
|
||||
vars.procData = JSON.stringify(scripts[0]);
|
||||
res.render(obj.VIEWS + 'procedit', vars);
|
||||
} else {
|
||||
vars.scriptData = JSON.stringify(scripts[0]);
|
||||
res.render(obj.VIEWS + 'scriptedit', vars);
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else if (req.query.schedule == 1) {
|
||||
var vars = {};
|
||||
res.render(obj.VIEWS + 'schedule', vars);
|
||||
return;
|
||||
}
|
||||
// default user view (tree)
|
||||
vars.scriptTree = 'null';
|
||||
obj.db.getScriptTree()
|
||||
.then(function(tree) {
|
||||
vars.scriptTree = JSON.stringify(tree);
|
||||
res.render(obj.VIEWS + 'user', vars);
|
||||
});
|
||||
return;
|
||||
} else if (req.query.include == 1) {
|
||||
switch (req.query.path.split('/').pop().split('.').pop()) {
|
||||
case 'css': res.contentType('text/css'); break;
|
||||
case 'js': res.contentType('text/javascript'); break;
|
||||
}
|
||||
res.sendFile(__dirname + '/includes/' + req.query.path); // don't freak out. Express covers any path issues.
|
||||
return;
|
||||
}
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
};
|
||||
|
||||
obj.historyData = function (message) {
|
||||
if (typeof pluginHandler.scripttask.loadHistory == 'function') pluginHandler.scripttask.loadHistory(message);
|
||||
if (typeof pluginHandler.scripttask.loadSchedule == 'function') pluginHandler.scripttask.loadSchedule(message);
|
||||
};
|
||||
|
||||
obj.variableData = function (message) {
|
||||
if (typeof pluginHandler.scripttask.loadVariables == 'function') pluginHandler.scripttask.loadVariables(message);
|
||||
};
|
||||
|
||||
obj.determineNextJobTime = function(s) {
|
||||
var nextTime = null;
|
||||
var nowTime = Math.floor(new Date() / 1000);
|
||||
|
||||
// special case: we've reached the end of our run
|
||||
if (s.endAt !== null && s.endAt <= nowTime) {
|
||||
return nextTime;
|
||||
}
|
||||
|
||||
switch (s.recur) {
|
||||
case 'once':
|
||||
if (s.nextRun == null) nextTime = s.startAt;
|
||||
else nextTime = null;
|
||||
break;
|
||||
case 'minutes':
|
||||
/*var lRun = s.nextRun || nowTime;
|
||||
if (lRun == null) lRun = nowTime;
|
||||
nextTime = lRun + (s.interval * 60);
|
||||
if (s.startAt > nextTime) nextTime = s.startAt;*/
|
||||
if (s.nextRun == null) { // hasn't run yet, set to start time
|
||||
nextTime = s.startAt;
|
||||
break;
|
||||
}
|
||||
nextTime = s.nextRun + (s.interval * 60);
|
||||
// this prevents "catch-up" tasks being scheduled if an endpoint is offline for a long period of time
|
||||
// e.g. always make sure the next scheduled time is relevant to the scheduled interval, but in the future
|
||||
if (nextTime < nowTime) {
|
||||
// initially I was worried about this causing event loop lockups
|
||||
// if there was a long enough time gap. Testing over 50 years of backlog for a 3 min interval
|
||||
// still ran under a fraction of a second. Safe to say this approach is safe! (~8.5 million times)
|
||||
while (nextTime < nowTime) {
|
||||
nextTime = nextTime + (s.interval * 60);
|
||||
}
|
||||
}
|
||||
if (s.startAt > nextTime) nextTime = s.startAt;
|
||||
break;
|
||||
case 'hourly':
|
||||
if (s.nextRun == null) { // hasn't run yet, set to start time
|
||||
nextTime = s.startAt;
|
||||
break;
|
||||
}
|
||||
nextTime = s.nextRun + (s.interval * 60 * 60);
|
||||
if (nextTime < nowTime) {
|
||||
while (nextTime < nowTime) {
|
||||
nextTime = nextTime + (s.interval * 60 * 60);
|
||||
}
|
||||
}
|
||||
if (s.startAt > nextTime) nextTime = s.startAt;
|
||||
break;
|
||||
case 'daily':
|
||||
if (s.nextRun == null) { // hasn't run yet, set to start time
|
||||
nextTime = s.startAt;
|
||||
break;
|
||||
}
|
||||
nextTime = s.nextRun + (s.interval * 60 * 60 * 24);
|
||||
if (nextTime < nowTime) {
|
||||
while (nextTime < nowTime) {
|
||||
nextTime = nextTime + (s.interval * 60 * 60 * 24);
|
||||
}
|
||||
}
|
||||
if (s.startAt > nextTime) nextTime = s.startAt;
|
||||
break;
|
||||
case 'weekly':
|
||||
var tempDate = new Date();
|
||||
var nowDate = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
|
||||
|
||||
if (s.daysOfWeek.length == 0) {
|
||||
nextTime = null;
|
||||
break;
|
||||
}
|
||||
s.daysOfWeek = s.daysOfWeek.map(function (el) { Number(el) });
|
||||
var baseTime = s.startAt;
|
||||
//console.log('dow is ', s.daysOfWeek);
|
||||
var lastDayOfWeek = Math.max(...s.daysOfWeek);
|
||||
var startX = 0;
|
||||
//console.log('ldow is ', lastDayOfWeek);
|
||||
if (s.nextRun != null) {
|
||||
baseTime = s.nextRun;
|
||||
//console.log('basetime 2: ', baseTime);
|
||||
if (nowDate.getDay() == lastDayOfWeek) {
|
||||
baseTime = baseTime + ( s.interval * 604800 ) - (lastDayOfWeek * 86400);
|
||||
//console.log('basetime 3: ', baseTime);
|
||||
}
|
||||
startX = 0;
|
||||
} else if (s.startAt < nowTime) {
|
||||
baseTime = Math.floor(nowDate.getTime() / 1000);
|
||||
//console.log('basetime 4: ', baseTime);
|
||||
}
|
||||
//console.log('startX is: ', startX);
|
||||
//var secondsFromMidnight = nowTimeDate.getSeconds() + (nowTimeDate.getMinutes() * 60) + (nowTimeDate.getHours() * 60 * 60);
|
||||
//console.log('seconds from midnight: ', secondsFromMidnight);
|
||||
//var dBaseTime = new Date(0); dBaseTime.setUTCSeconds(baseTime);
|
||||
//var dMidnight = new Date(dBaseTime.getFullYear(), dBaseTime.getMonth(), dBaseTime.getDate());
|
||||
//baseTime = Math.floor(dMidnight.getTime() / 1000);
|
||||
for (var x = startX; x <= 7; x++){
|
||||
var checkDate = baseTime + (86400 * x);
|
||||
var d = new Date(0); d.setUTCSeconds(checkDate);
|
||||
var dm = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
||||
|
||||
console.log('testing date: ', dm.toLocaleString()); // dMidnight.toLocaleString());
|
||||
//console.log('if break check :', (s.daysOfWeek.indexOf(d.getDay()) !== -1 && checkDate >= nowTime));
|
||||
//console.log('checkDate vs nowTime: ', (checkDate - nowTime), ' if positive, nowTime is less than checkDate');
|
||||
if (s.nextRun == null && s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() >= nowDate.getTime()) break;
|
||||
if (s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() > nowDate.getTime()) break;
|
||||
//if (s.daysOfWeek.indexOf(d.getDay()) !== -1 && Math.floor(d.getTime() / 1000) >= nowTime) break;
|
||||
}
|
||||
var sa = new Date(0); sa.setUTCSeconds(s.startAt);
|
||||
var sad = new Date(sa.getFullYear(), sa.getMonth(), sa.getDate());
|
||||
var diff = (sa.getTime() - sad.getTime()) / 1000;
|
||||
nextTime = Math.floor(dm.getTime() / 1000) + diff;
|
||||
//console.log('next schedule is ' + d.toLocaleString());
|
||||
break;
|
||||
default:
|
||||
nextTime = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (s.endAt != null && nextTime > s.endAt) nextTime = null; // if the next time reaches the bound of the endAt time, nullify
|
||||
|
||||
return nextTime;
|
||||
};
|
||||
|
||||
obj.makeJobsFromSchedules = function(scheduleId) {
|
||||
//obj.debug('ScriptTask', 'makeJobsFromSchedules starting');
|
||||
return obj.db.getSchedulesDueForJob(scheduleId)
|
||||
.then(function(schedules) {
|
||||
//obj.debug('ScriptTask', 'Found ' + schedules.length + ' schedules to process. Current time is: ' + Math.floor(new Date() / 1000));
|
||||
if (schedules.length) {
|
||||
schedules.forEach(function(s) {
|
||||
var nextJobTime = obj.determineNextJobTime(s);
|
||||
var nextJobScheduled = false;
|
||||
if (nextJobTime === null) {
|
||||
//obj.debug('ScriptTask', 'Removing Job Schedule for', JSON.stringify(s));
|
||||
obj.db.removeJobSchedule(s._id);
|
||||
} else {
|
||||
//obj.debug('ScriptTask', 'Scheduling Job for', JSON.stringify(s));
|
||||
obj.db.get(s.scriptId)
|
||||
.then(function(scripts) {
|
||||
// if a script is scheduled to run, but a previous run hasn't completed,
|
||||
// don't schedule another job for the same (device probably offline).
|
||||
// results in the minimum jobs running once an agent comes back online.
|
||||
return obj.db.getIncompleteJobsForSchedule(s._id)
|
||||
.then(function(jobs) {
|
||||
if (jobs.length > 0) { /* obj.debug('Task', 'Skipping job creation'); */ return Promise.resolve(); }
|
||||
else { /* obj.debug('Task', 'Creating new job'); */ nextJobScheduled = true; return obj.db.addJob( { scriptId: s.scriptId, scriptName: scripts[0].name, node: s.node, runBy: s.scheduledBy, dontQueueUntil: nextJobTime, jobSchedule: s._id } ); }
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
if (nextJobScheduled) { /* obj.debug('Plugin', 'ScriptTask', 'Updating nextRun time'); */ return obj.db.update(s._id, { nextRun: nextJobTime }); }
|
||||
else { /* obj.debug('Plugin', 'ScriptTask', 'NOT updating nextRun time'); */ return Promise.resolve(); }
|
||||
})
|
||||
.then(function() {
|
||||
obj.updateFrontEnd( { scriptId: s.scriptId, nodeId: s.node } );
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Error managing job schedules: ', ex); });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
obj.deleteElement = function (command) {
|
||||
var delObj = null;
|
||||
obj.db.get(command.id)
|
||||
.then(function(found) {
|
||||
var file = found[0];
|
||||
delObj = {...{}, ...found[0]};
|
||||
return file;
|
||||
})
|
||||
.then(function(file) {
|
||||
if (file.type == 'folder') return obj.db.deleteByPath(file.path); //@TODO delete schedules for scripts within folders
|
||||
if (file.type == 'script') return obj.db.deleteSchedulesForScript(file._id);
|
||||
if (file.type == 'jobSchedule') return obj.db.deletePendingJobsForSchedule(file._id);
|
||||
})
|
||||
.then(function() {
|
||||
return obj.db.delete(command.id)
|
||||
})
|
||||
.then(function() {
|
||||
var updateObj = { tree: true };
|
||||
if (delObj.type == 'jobSchedule') {
|
||||
updateObj.scriptId = delObj.scriptId;
|
||||
updateObj.nodeId = delObj.node;
|
||||
}
|
||||
return obj.updateFrontEnd( updateObj );
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Error deleting ', ex.stack); });
|
||||
};
|
||||
|
||||
// Process 'task' commands received by an agent
|
||||
obj.agentAction = function (command, agent) {
|
||||
console.log('task-agentAction', command);
|
||||
switch (command.subaction) {
|
||||
case 'getScript':
|
||||
// TODO
|
||||
break;
|
||||
case 'clearAllPendingTasks':
|
||||
// TODO
|
||||
break;
|
||||
case 'taskComplete':
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
obj.serveraction = function(command, myparent, grandparent) {
|
||||
switch (command.subaction) {
|
||||
case 'addScript':
|
||||
obj.db.addScript(command.name, command.content, command.path, command.filetype)
|
||||
.then(function() { obj.updateFrontEnd( { tree: true } ); });
|
||||
break;
|
||||
case 'new':
|
||||
var parent_path = '', new_path = '';
|
||||
obj.db.get(command.parent_id)
|
||||
.then(function(found) { if (found.length > 0) { var file = found[0]; parent_path = file.path; } else { parent_path = 'Shared'; } })
|
||||
.then(function () { obj.db.addScript(command.name, '', parent_path, command.filetype); })
|
||||
.then(function() { obj.updateFrontEnd( { tree: true } ); });
|
||||
break;
|
||||
case 'rename':
|
||||
obj.db.get(command.id)
|
||||
.then(function(docs) {
|
||||
var doc = docs[0];
|
||||
if (doc.type == 'folder') {
|
||||
console.log('old', doc.path, 'new', doc.path.replace(doc.path, command.name));
|
||||
return obj.db.update(command.id, { path: doc.path.replace(doc.name, command.name) })
|
||||
.then(function() { // update sub-items
|
||||
return obj.db.getByPath(doc.path)
|
||||
})
|
||||
.then(function(found) {
|
||||
if (found.length > 0) {
|
||||
var proms = [];
|
||||
found.forEach(function(f) { proms.push(obj.db.update(f._id, { path: doc.path.replace(doc.name, command.name) } )); })
|
||||
return Promise.all(proms);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
obj.db.update(command.id, { name: command.name })
|
||||
})
|
||||
.then(function() {
|
||||
return obj.db.updateScriptJobName(command.id, command.name);
|
||||
})
|
||||
.then(function() {
|
||||
obj.updateFrontEnd( { scriptId: command.id, nodeId: command.currentNodeId, tree: true } );
|
||||
});
|
||||
break;
|
||||
case 'move':
|
||||
var toPath = null, fromPath = null, parentType = null;
|
||||
obj.db.get(command.to)
|
||||
.then(function(found) { // get target data
|
||||
if (found.length > 0) {
|
||||
var file = found[0];
|
||||
toPath = file.path;
|
||||
} else throw Error('Target destination not found');
|
||||
})
|
||||
.then(function() { // get item to be moved
|
||||
return obj.db.get(command.id);
|
||||
})
|
||||
.then(function(found) { // set item to new location
|
||||
var file = found[0];
|
||||
if (file.type == 'folder') {
|
||||
fromPath = file.path;
|
||||
toPath += '/' + file.name;
|
||||
parentType = 'folder';
|
||||
if (file.name == 'Shared' && file.path == 'Shared') throw Error('Cannot move top level directory: Shared');
|
||||
}
|
||||
return obj.db.update(command.id, { path: toPath } );
|
||||
})
|
||||
.then(function() { // update sub-items
|
||||
return obj.db.getByPath(fromPath)
|
||||
})
|
||||
.then(function(found) {
|
||||
if (found.length > 0) {
|
||||
var proms = [];
|
||||
found.forEach(function(f) {
|
||||
proms.push(obj.db.update(f._id, { path: toPath } ));
|
||||
})
|
||||
return Promise.all(proms);
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
return obj.updateFrontEnd( { tree: true } );
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Error moving ', ex.stack); });
|
||||
break;
|
||||
case 'newFolder':
|
||||
var parent_path = '';
|
||||
var new_path = '';
|
||||
|
||||
obj.db.get(command.parent_id)
|
||||
.then(function(found) {
|
||||
if (found.length > 0) {
|
||||
var file = found[0];
|
||||
parent_path = file.path;
|
||||
} else {
|
||||
parent_path = 'Shared';
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
new_path = parent_path + '/' + command.name;
|
||||
})
|
||||
.then(function() {
|
||||
return obj.db.addFolder(command.name, new_path);
|
||||
})
|
||||
.then(function () {
|
||||
return obj.updateFrontEnd( { tree: true } );
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Error creating new folder ', ex.stack); });
|
||||
break;
|
||||
case 'delete':
|
||||
obj.deleteElement(command);
|
||||
break;
|
||||
case 'addScheduledJob':
|
||||
/* {
|
||||
scriptId: scriptId,
|
||||
node: s,
|
||||
scheduledBy: myparent.user.name,
|
||||
recur: command.recur, // [once, minutes, hourly, daily, weekly, monthly]
|
||||
interval: x,
|
||||
daysOfWeek: x, // only used for weekly recur val
|
||||
// onTheXDay: x, // only used for monthly
|
||||
startAt: x,
|
||||
endAt: x,
|
||||
runCountLimit: x,
|
||||
lastRun: x,
|
||||
nextRun: x,
|
||||
type: "scheduledJob"
|
||||
} */
|
||||
var sj = command.schedule;
|
||||
|
||||
var sched = {
|
||||
scriptId: command.scriptId,
|
||||
node: null,
|
||||
scheduledBy: myparent.user.name,
|
||||
recur: sj.recur,
|
||||
interval: sj.interval,
|
||||
daysOfWeek: sj.dayVals,
|
||||
startAt: sj.startAt,
|
||||
endAt: sj.endAt,
|
||||
lastRun: null,
|
||||
nextRun: null,
|
||||
type: "jobSchedule"
|
||||
};
|
||||
var sel = command.nodes;
|
||||
var proms = [];
|
||||
if (Array.isArray(sel)) {
|
||||
sel.forEach(function(s) {
|
||||
var sObj = {...sched, ...{ node: s }};
|
||||
proms.push(obj.db.addJobSchedule( sObj ));
|
||||
});
|
||||
} else { test.push(sObj);
|
||||
proms.push(obj.db.addJobSchedule( sObj ));
|
||||
}
|
||||
Promise.all(proms)
|
||||
.then(function() {
|
||||
obj.makeJobsFromSchedules();
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Error adding schedules. The error was: ', ex); });
|
||||
break;
|
||||
case 'runScript':
|
||||
var scriptId = command.scriptId;
|
||||
var sel = command.nodes;
|
||||
var proms = [];
|
||||
if (Array.isArray(sel)) {
|
||||
sel.forEach(function(s) {
|
||||
proms.push(obj.db.addJob( { scriptId: scriptId, node: s, runBy: myparent.user.name } ));
|
||||
});
|
||||
} else {
|
||||
proms.push(obj.db.addJob( { scriptId: scriptId, node: sel, runBy: myparent.user.name } ));
|
||||
}
|
||||
Promise.all(proms)
|
||||
.then(function() {
|
||||
return obj.db.get(scriptId);
|
||||
})
|
||||
.then(function(scripts) {
|
||||
return obj.db.updateScriptJobName(scriptId, scripts[0].name);
|
||||
})
|
||||
.then(function() {
|
||||
obj.resetQueueTimer();
|
||||
obj.queueRun();
|
||||
obj.updateFrontEnd( { scriptId: scriptId, nodeId: command.currentNodeId } );
|
||||
});
|
||||
break;
|
||||
case 'getScript':
|
||||
//obj.debug('ScriptTask', 'getScript Triggered', JSON.stringify(command));
|
||||
obj.db.get(command.scriptId)
|
||||
.then(function(script) {
|
||||
myparent.send(JSON.stringify({
|
||||
action: 'task',
|
||||
subaction: 'cacheScript',
|
||||
nodeid: myparent.dbNodeKey,
|
||||
rights: true,
|
||||
sessionid: true,
|
||||
script: script[0]
|
||||
}));
|
||||
});
|
||||
break;
|
||||
case 'jobComplete':
|
||||
//obj.debug('ScriptTask', 'jobComplete Triggered', JSON.stringify(command));
|
||||
var jobNodeHistory = null, scriptHistory = null;
|
||||
var jobId = command.jobId, retVal = command.retVal, errVal = command.errVal, dispatchTime = command.dispatchTime;
|
||||
var completeTime = Math.floor(new Date() / 1000);
|
||||
obj.db.update(jobId, {
|
||||
completeTime: completeTime,
|
||||
returnVal: retVal,
|
||||
errorVal: errVal,
|
||||
dispatchTime: dispatchTime
|
||||
})
|
||||
.then(function() {
|
||||
return obj.db.get(jobId)
|
||||
.then(function(jobs) {
|
||||
return Promise.resolve(jobs[0].jobSchedule);
|
||||
})
|
||||
.then(function(sId) {
|
||||
if (sId == null) return Promise.resolve();
|
||||
return obj.db.update(sId, { lastRun: completeTime } )
|
||||
.then(function() {
|
||||
obj.makeJobsFromSchedules(sId);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
obj.updateFrontEnd( { scriptId: command.scriptId, nodeId: myparent.dbNodeKey } );
|
||||
})
|
||||
.catch(function(ex) { console.log('Task: Failed to complete job. ', ex); });
|
||||
// update front end by eventing
|
||||
break;
|
||||
case 'loadNodeHistory':
|
||||
obj.updateFrontEnd( { nodeId: command.nodeId } );
|
||||
break;
|
||||
case 'loadScriptHistory':
|
||||
obj.updateFrontEnd( { scriptId: command.scriptId } );
|
||||
break;
|
||||
case 'editScript':
|
||||
obj.db.update(command.scriptId, { type: command.scriptType, name: command.scriptName, content: command.scriptContent })
|
||||
.then(function() { obj.updateFrontEnd( { scriptId: command.scriptId, tree: true } ); });
|
||||
break;
|
||||
case 'clearAllPendingJobs':
|
||||
obj.db.deletePendingJobsForNode(myparent.dbNodeKey);
|
||||
break;
|
||||
case 'loadVariables':
|
||||
obj.updateFrontEnd( { variables: true } );
|
||||
break;
|
||||
case 'newVar':
|
||||
obj.db.addVariable(command.name, command.scope, command.scopeTarget, command.value)
|
||||
.then(function() { obj.updateFrontEnd( { variables: true } ); })
|
||||
break;
|
||||
case 'editVar':
|
||||
obj.db.update(command.id, {
|
||||
name: command.name,
|
||||
scope: command.scope,
|
||||
scopeTarget: command.scopeTarget,
|
||||
value: command.value
|
||||
})
|
||||
.then(function() { obj.updateFrontEnd( { variables: true } ); })
|
||||
break;
|
||||
case 'deleteVar':
|
||||
obj.db.delete(command.id)
|
||||
.then(function() { obj.updateFrontEnd( { variables: true } ); })
|
||||
break;
|
||||
default:
|
||||
console.log('Task: unknown action');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/public/tail.DateTime/tail.datetime-default-blue.min.css" />
|
||||
<style>
|
||||
body {
|
||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#scriptContent {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
#schedContentC {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#controlBar button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#scriptNameC {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#scriptName {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#controlBar {
|
||||
padding: 5px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#left {
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#right {
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #036;
|
||||
}
|
||||
|
||||
#intervalListC {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#daysListC {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.rOpt {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#daysListC {
|
||||
display: inline-grid;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="doOnLoad();">
|
||||
<script type="text/javascript" src="/public/tail.DateTime/tail.datetime.min.js"></script>
|
||||
<div id="scriptTaskSchedule">
|
||||
<div id="controlBar">
|
||||
<button onclick="goSave();">Schedule</button>
|
||||
<button onclick="goCancel();">Cancel</button>
|
||||
</div>
|
||||
<div id="schedContentC">
|
||||
<div id="left">
|
||||
<span class="oTitle">Recurrence</span>
|
||||
<ul id="intervalListC">
|
||||
<li><label><input onclick="intervalSelected(this);" type="radio" checked name="recur" value="once">Once</label></li>
|
||||
<li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="minutes">Minutes</label></li>
|
||||
<li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="hourly">Hourly</label></li>
|
||||
<li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="daily">Daily</label></li>
|
||||
<li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="weekly">Weekly</label></li>
|
||||
<!-- li><label><input type="radio" name="recur" value="monthly">Monthly</label></li -->
|
||||
</ul>
|
||||
</div>
|
||||
<div id="right">
|
||||
<div class="rOpt">
|
||||
<span class="oTitle">Start: </span>
|
||||
<input type="text" class="datePick" id="startDate" value="" />
|
||||
<input type="text" class="timePick" id="startTime" value="" />
|
||||
</div>
|
||||
<div class="rOpt" id="intervalC" style="display: none;">
|
||||
<span class="oTitle">Every: </span>
|
||||
<input type="text" id="interval" value="1" /> <span id="hintText"></span>
|
||||
</div>
|
||||
<div class="rOpt" id="endC" style="display: none;">
|
||||
<span class="oTitle">End: </span>
|
||||
<input type="text" class="datePick" id="endDate" value="" />
|
||||
<input type="text" class="timePick" id="endTime" value="" />
|
||||
<label><input type="checkbox" id="endNever" checked onclick="checkEndNever(this);" /> Never</label>
|
||||
</div>
|
||||
<div class="rOpt" id="daysC" style="display: none;">
|
||||
<span class="oTitle">Days: </span>
|
||||
<ul id="daysListC">
|
||||
<li><label><input type="checkbox" name="days[]" value="0"> Sunday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="1"> Monday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="2"> Tuesday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="3"> Wednesday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="4"> Thursday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="5"> Friday</label></li>
|
||||
<li><label><input type="checkbox" name="days[]" value="6"> Saturday</label></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
function checkEndNever(el) {
|
||||
if (el.checked) {
|
||||
Q('endDate').value = '';
|
||||
Q('endTime').value = '';
|
||||
}
|
||||
}
|
||||
function setTimePick() {
|
||||
var d = new Date();
|
||||
document.getElementById("startDate").value = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();
|
||||
document.getElementById("startTime").value = d.getHours() + ':' + d.getMinutes();
|
||||
tail.DateTime(".datePick", { position: "bottom", dateStart: Date(), timeFormat: false });
|
||||
tail.DateTime(".timePick", { position: "bottom", dateFormat: false, timeFormat: "HH:ii", timeStepMinutes: 15 });
|
||||
tail.datetime.inst[document.getElementById('endDate').getAttribute('data-tail-datetime')].on('change', function () {
|
||||
document.getElementById('endNever').checked = false;
|
||||
});
|
||||
tail.datetime.inst[document.getElementById('endTime').getAttribute('data-tail-datetime')].on('change', function () {
|
||||
document.getElementById('endNever').checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
function doOnLoad() {
|
||||
try {
|
||||
if (scriptId == null) {
|
||||
alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
|
||||
goCancel();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
|
||||
goCancel();
|
||||
return;
|
||||
}
|
||||
setTimePick();
|
||||
}
|
||||
|
||||
function intervalSelected(el) {
|
||||
var v = el.value;
|
||||
switch (v) {
|
||||
case 'once':
|
||||
QV('intervalC', false);
|
||||
QV('endC', false);
|
||||
QV('daysC', false);
|
||||
break;
|
||||
case 'minutes':
|
||||
QV('intervalC', true);
|
||||
QV('endC', true);
|
||||
QV('daysC', false);
|
||||
QH('hintText', 'minute(s)');
|
||||
break;
|
||||
case 'hourly':
|
||||
QV('intervalC', true);
|
||||
QV('endC', true);
|
||||
QV('daysC', false);
|
||||
QH('hintText', 'hour(s)');
|
||||
break;
|
||||
case 'daily':
|
||||
QV('intervalC', true);
|
||||
QV('endC', true);
|
||||
QV('daysC', false);
|
||||
QH('hintText', 'day(s)');
|
||||
break;
|
||||
case 'weekly':
|
||||
QV('intervalC', true);
|
||||
QV('endC', true);
|
||||
QV('daysC', true);
|
||||
QH('hintText', 'week(s)');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function goSave() {
|
||||
var o = {};
|
||||
var recurEls = document.getElementsByName("recur");
|
||||
recurEls.forEach(function (el) {
|
||||
if (el.checked) o.recur = el.value;
|
||||
});
|
||||
switch (o.recur) {
|
||||
case 'once':
|
||||
o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
|
||||
o.startAt = Math.floor(o.startAt / 1000);
|
||||
break;
|
||||
case 'minutes':
|
||||
case 'hourly':
|
||||
case 'daily':
|
||||
o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
|
||||
o.startAt = Math.floor(o.startAt / 1000);
|
||||
o.interval = Number(Q('interval').value);
|
||||
if (Q('endNever').checked) o.endAt = null;
|
||||
else {
|
||||
o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
|
||||
o.endAt = Math.floor(o.endAt / 1000);
|
||||
}
|
||||
break;
|
||||
case 'weekly':
|
||||
o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
|
||||
o.startAt = Math.floor(o.startAt / 1000);
|
||||
o.interval = Number(Q('interval').value);
|
||||
if (Q('endNever').checked) o.endAt = null;
|
||||
else {
|
||||
o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
|
||||
o.endAt = Math.floor(o.endAt / 1000);
|
||||
}
|
||||
var dayEls = document.getElementsByName("days[]");
|
||||
o.dayVals = [];
|
||||
if (dayEls.length) {
|
||||
dayEls.forEach(function (de) {
|
||||
if (de.checked) o.dayVals.push(de.value);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
o.scriptId = scriptId;
|
||||
o.nodes = nodes;
|
||||
|
||||
window.opener.schedCallback(o);
|
||||
window.close();
|
||||
}
|
||||
|
||||
function goCancel() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,73 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#scriptContent {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
#scriptContentC {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#controlBar button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#scriptNameC {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#scriptName {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#controlBar {
|
||||
padding: 5px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #036;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="doOnLoad();">
|
||||
<div id="scriptTaskScriptEdit">
|
||||
<div id="scriptNameC">Script Name: <input type="text" value="" id="scriptName" /></div>
|
||||
<div id="controlBar">
|
||||
<button onclick="goSave();">Save</button>
|
||||
<button onclick="goClose();">Close</button>
|
||||
</div>
|
||||
<div id="scriptContentC">
|
||||
<textarea id="scriptContent"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var scriptData = {{{scriptData}}};
|
||||
|
||||
function doOnLoad() {
|
||||
//QH('scriptContent', scriptData.content);
|
||||
Q('scriptContent').value = scriptData.content;
|
||||
Q('scriptName').value = scriptData.name;
|
||||
}
|
||||
|
||||
function goSave() {
|
||||
scriptData.content = Q('scriptContent').value;
|
||||
scriptData.name = Q('scriptName').value;
|
||||
window.opener.callback(scriptData);
|
||||
//goClose();
|
||||
}
|
||||
|
||||
function goClose() {
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue