import crontab db

pull/1/head
Suresh Alse 2015-06-14 02:08:20 +05:30
parent 41ca8a72c7
commit 12679779cf
10 changed files with 134 additions and 19 deletions

View File

@ -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
View File

@ -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'))
})

View File

@ -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);
});
}

View File

@ -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"

View File

@ -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 = "";

View File

@ -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);
});
}

View File

@ -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
}

View File

@ -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>

View File

@ -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">

View File

@ -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>