import crontab db
parent
41ca8a72c7
commit
12679779cf
|
@ -14,9 +14,8 @@ Editing the plain text crontab is error prone for managing jobs, e.g., adding jo
|
|||
TODO
|
||||
====
|
||||
1. Run jobs as different user
|
||||
2. Online backup
|
||||
3. Profiling jobs
|
||||
4. Logs
|
||||
|
||||
2. Profiling jobs
|
||||
3. Logs
|
||||
4. Importing from existing crontab file
|
||||
|
||||
|
||||
|
|
39
app.js
39
app.js
|
@ -3,6 +3,12 @@ var app = express();
|
|||
var crontab = require("./crontab");
|
||||
var restore = require("./restore");
|
||||
|
||||
var path = require('path');
|
||||
var mime = require('mime');
|
||||
var fs = require('fs');
|
||||
var busboy = require('connect-busboy'); // for file upload
|
||||
|
||||
|
||||
// include the routes
|
||||
var routes = require("./routes").routes;
|
||||
|
||||
|
@ -14,6 +20,7 @@ app.use( bodyParser.json() ); // to support JSON-encoded bodies
|
|||
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
|
||||
extended: true
|
||||
}));
|
||||
app.use(busboy()) // to support file uploads
|
||||
|
||||
// include all folders
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
@ -92,6 +99,38 @@ app.get(routes.restore_backup, function(req, res) {
|
|||
res.end();
|
||||
})
|
||||
|
||||
app.get(routes.export, function(req, res) {
|
||||
var file = __dirname + '/crontabs/crontab.db';
|
||||
|
||||
var filename = path.basename(file);
|
||||
var mimetype = mime.lookup(file);
|
||||
|
||||
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
|
||||
res.setHeader('Content-type', mimetype);
|
||||
|
||||
var filestream = fs.createReadStream(file);
|
||||
filestream.pipe(res);
|
||||
})
|
||||
|
||||
|
||||
app.post(routes.import, function(req, res) {
|
||||
var fstream;
|
||||
req.pipe(req.busboy);
|
||||
req.busboy.on('file', function (fieldname, file, filename) {
|
||||
fstream = fs.createWriteStream(__dirname + '/crontabs/crontab.db');
|
||||
file.pipe(fstream);
|
||||
fstream.on('close', function () {
|
||||
crontab.reload_db();
|
||||
res.redirect(routes.root);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
app.get(routes.import_crontab, function(req, res) {
|
||||
crontab.import_crontab()
|
||||
res.end();
|
||||
})
|
||||
|
||||
app.listen(app.get('port'), function() {
|
||||
console.log("Crontab UI is running at localhost:" + app.get('port'))
|
||||
})
|
||||
|
|
51
crontab.js
51
crontab.js
|
@ -5,19 +5,23 @@ db.loadDatabase(function (err) {
|
|||
});
|
||||
var exec = require('child_process').exec;
|
||||
var fs = require('fs');
|
||||
var cron_parser = require("cron-parser")
|
||||
|
||||
crontab = function(name, command, schedule, stopped){
|
||||
var data = {};
|
||||
data.name = name;
|
||||
data.command = command;
|
||||
data.schedule = schedule;
|
||||
if(stopped != null) data.stopped = stopped;
|
||||
if(stopped != null) {
|
||||
data.stopped = stopped;
|
||||
}
|
||||
data.timestamp = (new Date()).toString();
|
||||
return data;
|
||||
}
|
||||
|
||||
exports.create_new = function(name, command, schedule){
|
||||
var tab = crontab(name, command, schedule, false);
|
||||
tab.created = new Date().valueOf();
|
||||
db.insert(tab);
|
||||
}
|
||||
|
||||
|
@ -33,7 +37,13 @@ exports.remove = function(_id){
|
|||
db.remove({_id: _id}, {});
|
||||
}
|
||||
exports.crontabs = function(callback){
|
||||
db.find({}, function(err, docs){
|
||||
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
|
||||
docs[i].next = cron_parser.parseExpression(docs[i].schedule).next().toString();
|
||||
}
|
||||
callback(docs);
|
||||
});
|
||||
}
|
||||
|
@ -61,6 +71,21 @@ exports.get_backup_names = function(){
|
|||
}
|
||||
});
|
||||
|
||||
// Sort by date. Newest on top
|
||||
for(var i=0; i<backups.length; i++){
|
||||
var Ti = backups[i].split("backup")[1]
|
||||
Ti = new Date(Ti.substring(0, Ti.length-3)).valueOf();
|
||||
for(var j=0; j<i; j++){
|
||||
var Tj = backups[j].split("backup")[1]
|
||||
Tj = new Date(Tj.substring(0, Tj.length-3)).valueOf();
|
||||
if(Ti > Tj){
|
||||
var temp = backups[i];
|
||||
backups[i] = backups[j];
|
||||
backups[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backups;
|
||||
}
|
||||
|
||||
|
@ -74,6 +99,24 @@ exports.restore = function(db_name){
|
|||
db.loadDatabase(); // reload the database
|
||||
}
|
||||
|
||||
exports.import = function(){
|
||||
//TODO
|
||||
exports.reload_db= function(){
|
||||
db.loadDatabase();
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.import_crontab = function(){
|
||||
exec("crontab -l", function(error, stdout, stderr){
|
||||
var lines = stdout.split("\n");
|
||||
lines.forEach(function(line){
|
||||
/*
|
||||
trim the spaces at edges
|
||||
split the line based of space and tab
|
||||
remove empty splits
|
||||
If the first character is @
|
||||
|
||||
*/
|
||||
//if(line.indexOf("@")
|
||||
})
|
||||
console.log(stdout);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
"express": "latest",
|
||||
"ejs": "latest",
|
||||
"nedb": "latest",
|
||||
"body-parser": "latest"
|
||||
"body-parser": "latest",
|
||||
"mime": "latest",
|
||||
"cron-parser": "latest",
|
||||
"connect-busboy": "latest"
|
||||
},
|
||||
"engines": {
|
||||
"node": "latest"
|
||||
|
|
|
@ -10,8 +10,8 @@ function messageBox(body, title, ok_text, close_text, callback){
|
|||
$("#modal-body").html(body);
|
||||
$("#modal-title").html(title);
|
||||
if (ok_text) $("#modal-button").html(ok_text);
|
||||
$("#modal-button").show();
|
||||
if(close_text) $("#modal-close-button").html(close_text);
|
||||
$("#modal-button").unbind("click"); // remove existing events attached to this
|
||||
$("#modal-button").click(callback);
|
||||
$("#popup").modal("show");
|
||||
}
|
||||
|
@ -131,6 +131,12 @@ function restore_backup(db_name){
|
|||
});
|
||||
}
|
||||
|
||||
function import_db(){
|
||||
messageBox("<p> Do you want to import crontab?<br /> <b style='color:red'>NOTE: It is recommended to take a backup before this.</b> </p>", "Confirm import from crontab", null, null, function(){
|
||||
$('#import_file').click();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// script corresponding to job popup management
|
||||
var schedule = "";
|
||||
|
|
|
@ -8,7 +8,7 @@ exports.crontabs = function(db_name, callback){
|
|||
var db = new Datastore({ filename: __dirname + '/crontabs/' + db_name });
|
||||
db.loadDatabase(function (err) {
|
||||
});
|
||||
db.find({}, function(err, docs){
|
||||
db.find({}).sort({ created: -1 }).exec(function(err, docs){
|
||||
callback(docs);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,5 +8,8 @@ exports.routes = {
|
|||
"backup": "/backup",
|
||||
"restore": "/restore",
|
||||
"delete_backup": "/delete",
|
||||
"restore_backup": "/restore_backup"
|
||||
"restore_backup": "/restore_backup",
|
||||
"export": "/export",
|
||||
"import": "/import", // this is import from database
|
||||
"import_crontab": "/import_crontab", // this is from existing crontab
|
||||
}
|
||||
|
|
|
@ -20,14 +20,29 @@
|
|||
<% include navbar %>
|
||||
<div class="container">
|
||||
<h2>Cronjobs</h2>
|
||||
|
||||
<a class="btn btn-primary" onclick="newJob();"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> New</a>
|
||||
<a class="btn btn-info" onclick="doBackup();"><span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span> Backup</a>
|
||||
<form id="import_form" enctype="multipart/form-data" action="<%= JSON.parse(routes).import %>" method="post" style="display:none">
|
||||
<input type="file" id="import_file" name="import_file" onchange="$('#import_form').submit()"/>
|
||||
</form>
|
||||
<a class="btn btn-warning" onclick="import_db()"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Import</a>
|
||||
<a class="btn btn-warning" href="<%= JSON.parse(routes).export %>"><span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span> Export</a>
|
||||
<!--<a class="btn btn-info" onclick="import_crontab()"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Import from crontab</a>-->
|
||||
<a class="btn btn-success" onclick="setCrontab();"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Save to crontab</a>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th></th>
|
||||
<th>Job</th>
|
||||
<th>Time</th>
|
||||
<th>Last Modified</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<% var index = 1 %>
|
||||
<% JSON.parse(crontabs).forEach(function(crontab){ %>
|
||||
<!-- color based on crontab state -->
|
||||
<% if (!crontab.stopped) { %>
|
||||
|
@ -35,6 +50,11 @@
|
|||
<% } else { %>
|
||||
<tr style="background:#3A6DA6;color:#fff">
|
||||
<% } %>
|
||||
<td>
|
||||
<%= index %>.
|
||||
<% index += 1 %>
|
||||
<%= crontab._id %>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<% if (crontab.name) { %>
|
||||
|
@ -42,7 +62,7 @@
|
|||
<% } %>
|
||||
</td>
|
||||
<td><%= crontab.command %></td>
|
||||
<td><%= crontab.schedule %></td>
|
||||
<td><span style="cursor:pointer" data-toggle="tooltip" data-placement="bottom" title="<%= crontab.next %>"><%= crontab.schedule %></span></td>
|
||||
<td><%= crontab.timestamp %></td>
|
||||
<td>
|
||||
|
||||
|
@ -62,11 +82,6 @@
|
|||
|
||||
|
||||
</table>
|
||||
<a class="btn btn-primary" onclick="newJob();"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> New</a>
|
||||
<a class="btn btn-info" onclick="doBackup();"><span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span> Backup</a>
|
||||
<a class="btn btn-info"><span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span> Export</a>
|
||||
<a class="btn btn-info"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Import from crontab</a>
|
||||
<a class="btn btn-success" onclick="setCrontab();"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Save to crontab</a>
|
||||
</div>
|
||||
<% include popup.ejs %>
|
||||
</body>
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="#">Import</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Backups <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
|
|
@ -22,11 +22,13 @@
|
|||
<h2><%= db %></h2>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th></th>
|
||||
<th>Job</th>
|
||||
<th>Time</th>
|
||||
<th>Last Modified</th>
|
||||
</tr>
|
||||
<% var index = 1 %>
|
||||
<% JSON.parse(crontabs).forEach(function(crontab){ %>
|
||||
<!-- color based on crontab state -->
|
||||
<% if (!crontab.stopped) { %>
|
||||
|
@ -35,6 +37,12 @@
|
|||
<tr style="background:#3A6DA6;color:#fff">
|
||||
<% } %>
|
||||
|
||||
<td>
|
||||
<%= index %>.
|
||||
<% index += 1 %>
|
||||
<%= crontab._id %>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<% if (crontab.name) { %>
|
||||
<a class="btn" data-toggle="tooltip" data-placement="right" title="<%= crontab.name %>"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> </a>
|
||||
|
|
Loading…
Reference in New Issue