Merge b48b37dc6f
into c69ef51a04
commit
48b8f23691
287
crontab.js
287
crontab.js
|
@ -1,15 +1,22 @@
|
|||
/*jshint esversion: 6*/
|
||||
//load database
|
||||
var Datastore = require('nedb');
|
||||
const { Low, JSONFile } = require('lowdb');
|
||||
var path = require("path");
|
||||
|
||||
exports.db_folder = process.env.CRON_DB_PATH === undefined ? path.join(__dirname, "crontabs") : process.env.CRON_DB_PATH;
|
||||
console.log("Cron db path: " + exports.db_folder);
|
||||
exports.log_folder = path.join(exports.db_folder, 'logs');
|
||||
exports.env_file = path.join(exports.db_folder, 'env.db');
|
||||
exports.crontab_db_file = path.join(exports.db_folder, 'crontab.db');
|
||||
exports.crontab_db_file = path.join(exports.db_folder, 'crontab.json');
|
||||
|
||||
var db = new Datastore({ filename: exports.crontab_db_file});
|
||||
const adapter = new JSONFile(exports.crontab_db_file);
|
||||
const db = new Low(adapter);
|
||||
async function initDB() {
|
||||
await db.read();
|
||||
db.data = db.data || { crontabs: [] };
|
||||
await db.write();
|
||||
}
|
||||
initDB();
|
||||
var cronPath = "/tmp";
|
||||
if(process.env.CRON_PATH !== undefined) {
|
||||
console.log(`Path to crond files set using env variables ${process.env.CRON_PATH}`);
|
||||
|
@ -27,7 +34,7 @@ var cronstrue = require('cronstrue/i18n');
|
|||
var humanCronLocate = process.env.HUMANCRON ?? "en"
|
||||
|
||||
if (!fs.existsSync(exports.log_folder)){
|
||||
fs.mkdirSync(exports.log_folder);
|
||||
fs.mkdirSync(exports.log_folder);
|
||||
}
|
||||
|
||||
crontab = function(name, command, schedule, stopped, logging, mailing){
|
||||
|
@ -46,73 +53,84 @@ crontab = function(name, command, schedule, stopped, logging, mailing){
|
|||
return data;
|
||||
};
|
||||
|
||||
exports.create_new = function(name, command, schedule, logging, mailing){
|
||||
var tab = crontab(name, command, schedule, false, logging, mailing);
|
||||
tab.created = new Date().valueOf();
|
||||
tab.saved = false;
|
||||
db.insert(tab);
|
||||
exports.create_new = async function(name, command, schedule, logging, mailing){
|
||||
await initDB();
|
||||
var tab = crontab(name, command, schedule, false, logging, mailing);
|
||||
tab.created = new Date().valueOf();
|
||||
tab.saved = false;
|
||||
tab._id = Date.now().toString() + Math.random().toString(36).substr(2, 9);
|
||||
db.data.crontabs.push(tab);
|
||||
await db.write();
|
||||
};
|
||||
|
||||
exports.update = function(data){
|
||||
var tab = crontab(data.name, data.command, data.schedule, null, data.logging, data.mailing);
|
||||
tab.saved = false;
|
||||
db.update({_id: data._id}, tab);
|
||||
exports.update = async function(data){
|
||||
await initDB();
|
||||
var tab = crontab(data.name, data.command, data.schedule, null, data.logging, data.mailing);
|
||||
tab.saved = false;
|
||||
const idx = db.data.crontabs.findIndex(t => t._id === data._id);
|
||||
if (idx !== -1) {
|
||||
db.data.crontabs[idx] = { ...db.data.crontabs[idx], ...tab };
|
||||
await db.write();
|
||||
}
|
||||
};
|
||||
|
||||
exports.status = function(_id, stopped){
|
||||
db.update({_id: _id},{$set: {stopped: stopped, saved: false}});
|
||||
exports.status = async function(_id, stopped){
|
||||
await initDB();
|
||||
const idx = db.data.crontabs.findIndex(t => t._id === _id);
|
||||
if (idx !== -1) {
|
||||
db.data.crontabs[idx].stopped = stopped;
|
||||
db.data.crontabs[idx].saved = false;
|
||||
await db.write();
|
||||
}
|
||||
};
|
||||
|
||||
exports.remove = function(_id){
|
||||
db.remove({_id: _id}, {});
|
||||
exports.remove = async function(_id){
|
||||
await initDB();
|
||||
db.data.crontabs = db.data.crontabs.filter(t => t._id !== _id);
|
||||
await db.write();
|
||||
};
|
||||
|
||||
// Iterates through all the crontab entries in the db and calls the callback with the entries
|
||||
exports.crontabs = function(callback){
|
||||
db.find({}).sort({ created: -1 }).exec(function(err, docs){
|
||||
for(var i=0; i<docs.length; i++){
|
||||
if(docs[i].schedule == "@reboot")
|
||||
docs[i].next = "Next Reboot";
|
||||
else
|
||||
try {
|
||||
docs[i].human = cronstrue.toString(docs[i].schedule, { locale: humanCronLocate });
|
||||
docs[i].next = cron_parser.parseExpression(docs[i].schedule).next().toString();
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
docs[i].next = "invalid";
|
||||
}
|
||||
}
|
||||
callback(docs);
|
||||
});
|
||||
exports.crontabs = async function(callback){
|
||||
await initDB();
|
||||
let docs = db.data.crontabs.slice().sort((a, b) => b.created - a.created);
|
||||
for(let i=0; i<docs.length; i++){
|
||||
if(docs[i].schedule == "@reboot")
|
||||
docs[i].next = "Next Reboot";
|
||||
else
|
||||
try {
|
||||
docs[i].human = cronstrue.toString(docs[i].schedule, { locale: humanCronLocate });
|
||||
docs[i].next = cron_parser.parseExpression(docs[i].schedule).next().toString();
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
docs[i].next = "invalid";
|
||||
}
|
||||
}
|
||||
callback(docs);
|
||||
};
|
||||
|
||||
exports.get_crontab = function(_id, callback) {
|
||||
db.find({_id: _id}).exec(function(err, docs){
|
||||
callback(docs[0]);
|
||||
});
|
||||
exports.get_crontab = async function(_id, callback) {
|
||||
await initDB();
|
||||
const doc = db.data.crontabs.find(t => t._id === _id);
|
||||
callback(doc);
|
||||
};
|
||||
|
||||
exports.runjob = function(_id) {
|
||||
db.find({_id: _id}).exec(function(err, docs){
|
||||
let res = docs[0];
|
||||
|
||||
let env_vars = exports.get_env()
|
||||
|
||||
let crontab_job_string_command = make_command(res)
|
||||
|
||||
crontab_job_string_command = add_env_vars(env_vars, crontab_job_string_command)
|
||||
|
||||
console.log("Running job")
|
||||
console.log("ID: " + _id)
|
||||
console.log("Original command: " + res.command)
|
||||
console.log("Executed command: " + crontab_job_string_command)
|
||||
|
||||
exec(crontab_job_string_command, function(error, stdout, stderr){
|
||||
if (error) {
|
||||
console.log(error)
|
||||
}
|
||||
});
|
||||
});
|
||||
exports.runjob = async function(_id) {
|
||||
await initDB();
|
||||
let res = db.data.crontabs.find(t => t._id === _id);
|
||||
if (!res) return;
|
||||
let env_vars = exports.get_env();
|
||||
let crontab_job_string_command = make_command(res);
|
||||
crontab_job_string_command = add_env_vars(env_vars, crontab_job_string_command);
|
||||
console.log("Running job");
|
||||
console.log("ID: " + _id);
|
||||
console.log("Original command: " + res.command);
|
||||
console.log("Executed command: " + crontab_job_string_command);
|
||||
exec(crontab_job_string_command, function(error, stdout, stderr){
|
||||
if (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
make_command = function(tab) {
|
||||
|
@ -172,48 +190,47 @@ add_env_vars = function(env_vars, command) {
|
|||
}
|
||||
|
||||
// Set actual crontab file from the db
|
||||
exports.set_crontab = function(env_vars, callback) {
|
||||
exports.crontabs( function(tabs){
|
||||
var crontab_string = "";
|
||||
if (env_vars) {
|
||||
crontab_string += env_vars;
|
||||
crontab_string += "\n";
|
||||
}
|
||||
tabs.forEach(function(tab){
|
||||
if(!tab.stopped) {
|
||||
crontab_string += tab.schedule
|
||||
crontab_string += " "
|
||||
crontab_string += make_command(tab)
|
||||
crontab_string += "\n";
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFile(exports.env_file, env_vars, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
// In docker we're running as the root user, so we need to write the file as root and not crontab
|
||||
var fileName = process.env.CRON_IN_DOCKER !== undefined ? "root" : "crontab";
|
||||
fs.writeFile(path.join(cronPath, fileName), crontab_string, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
exec("crontab " + path.join(cronPath, fileName), function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return callback(err);
|
||||
}
|
||||
else {
|
||||
db.update({},{$set: {saved: true}}, {multi: true});
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
exports.set_crontab = async function(env_vars, callback) {
|
||||
await exports.crontabs(async function(tabs){
|
||||
var crontab_string = "";
|
||||
if (env_vars) {
|
||||
crontab_string += env_vars;
|
||||
crontab_string += "\n";
|
||||
}
|
||||
tabs.forEach(function(tab){
|
||||
if(!tab.stopped) {
|
||||
crontab_string += tab.schedule;
|
||||
crontab_string += " ";
|
||||
crontab_string += make_command(tab);
|
||||
crontab_string += "\n";
|
||||
}
|
||||
});
|
||||
fs.writeFile(exports.env_file, env_vars, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
var fileName = process.env.CRON_IN_DOCKER !== undefined ? "root" : "crontab";
|
||||
fs.writeFile(path.join(cronPath, fileName), crontab_string, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return callback(err);
|
||||
}
|
||||
exec("crontab " + path.join(cronPath, fileName), async function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return callback(err);
|
||||
}
|
||||
else {
|
||||
await initDB();
|
||||
db.data.crontabs.forEach(tab => tab.saved = true);
|
||||
await db.write();
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.get_backup_names = function(){
|
||||
|
@ -246,54 +263,42 @@ exports.backup = (callback) => {
|
|||
};
|
||||
|
||||
exports.restore = function(db_name){
|
||||
fs.createReadStream(path.join(exports.db_folder, db_name)).pipe(fs.createWriteStream(exports.crontab_db_file));
|
||||
db.loadDatabase(); // reload the database
|
||||
fs.createReadStream(path.join(exports.db_folder, db_name)).pipe(fs.createWriteStream(exports.crontab_db_file));
|
||||
// For lowdb, reload is just re-reading the file
|
||||
initDB();
|
||||
};
|
||||
|
||||
exports.reload_db = function(){
|
||||
db.loadDatabase();
|
||||
};
|
||||
|
||||
exports.get_env = function(){
|
||||
if (fs.existsSync(exports.env_file)) {
|
||||
return fs.readFileSync(exports.env_file , 'utf8').replace("\n", "\n");
|
||||
}
|
||||
return "";
|
||||
initDB();
|
||||
};
|
||||
|
||||
exports.import_crontab = function(){
|
||||
exec("crontab -l", function(error, stdout, stderr){
|
||||
var lines = stdout.split("\n");
|
||||
var namePrefix = new Date().getTime();
|
||||
|
||||
lines.forEach(function(line, index){
|
||||
line = line.replace(/\t+/g, ' ');
|
||||
var regex = /^((\@[a-zA-Z]+\s+)|(([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+))/;
|
||||
var command = line.replace(regex, '').trim();
|
||||
var schedule = line.replace(command, '').trim();
|
||||
|
||||
var is_valid = false;
|
||||
try { is_valid = cron_parser.parseString(line).expressions.length > 0; } catch (e){}
|
||||
|
||||
if(command && schedule && is_valid){
|
||||
var name = namePrefix + '_' + index;
|
||||
|
||||
db.findOne({ command: command, schedule: schedule }, function(err, doc) {
|
||||
if(err) {
|
||||
throw err;
|
||||
}
|
||||
if(!doc){
|
||||
exports.create_new(name, command, schedule, null);
|
||||
}
|
||||
else{
|
||||
doc.command = command;
|
||||
doc.schedule = schedule;
|
||||
exports.update(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
exec("crontab -l", async function(error, stdout, stderr){
|
||||
var lines = stdout.split("\n");
|
||||
var namePrefix = new Date().getTime();
|
||||
await initDB();
|
||||
for (let index = 0; index < lines.length; index++) {
|
||||
let line = lines[index].replace(/\t+/g, ' ');
|
||||
var regex = /^((\@[a-zA-Z]+\s+)|(([^ |