diff --git a/README.md b/README.md index 1dd77a1..1bd098d 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,22 @@ Editing the plain text crontab is error prone for managing jobs, e.g., adding jo ![flow](http://alseambusher.github.io/files/flow.gif) 1. Easy setup -2. Easy and safe adding, deleting or pausing jobs. Easy to maintain hundreds of jobs. -3. Backups -4. Download and schedule scripts which are online -5. Manage crontabs on multiple machines easily. No SSH, No copy-pasting. - -TODO -==== -1. Run jobs as different user -2. Online backup -3. Profiling jobs -4. Logs +2. Safe adding, deleting or pausing jobs. Easy to maintain hundreds of jobs. +3. Backup your crontabs. +4. Export crontab and deploy on other machines without much hassle. +Read [this](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html) to see more details. + +##Setup + + npm install crontab-ui + crontab-ui + +###TODO + +1. Run jobs as different user in one place. +2. Profiling jobs. +3. Logs. +4. Importing from existing crontab file. diff --git a/app.js b/app.js index 63749c7..9d10726 100755 --- a/app.js +++ b/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')) + console.log("Crontab UI is running at localhost:" + app.get('port')) }) diff --git a/bin/crontab-ui.js b/bin/crontab-ui.js new file mode 100755 index 0000000..bc2aa8c --- /dev/null +++ b/bin/crontab-ui.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +var crontab = require('../app.js'); diff --git a/crontab.js b/crontab.js index 6b5c6a0..9b7ad3f 100644 --- a/crontab.js +++ b/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 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); + }); } diff --git a/package.json b/package.json index 9c59d0d..6f77f30 100755 --- a/package.json +++ b/package.json @@ -1,19 +1,25 @@ { "name": "crontab-ui", - "version": "0.1.0", + "version": "0.1.1", "description": "Easy and safe way to manage your crontab file", "main": "index.js", "scripts": { - "start": "node index.js" + "start": "node app.js" }, "dependencies": { "express": "latest", "ejs": "latest", "nedb": "latest", - "body-parser": "latest" + "body-parser": "latest", + "mime": "latest", + "cron-parser": "latest", + "connect-busboy": "latest" }, "engines": { "node": "latest" + }, + "bin": { + "crontab-ui": "bin/crontab-ui.js" }, "repository": { "type": "git", diff --git a/public/js/script.js b/public/js/script.js index 49ac907..fcb2e53 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -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("

Do you want to import crontab?
NOTE: It is recommended to take a backup before this.

", "Confirm import from crontab", null, null, function(){ + $('#import_file').click(); + }); +} + // script corresponding to job popup management var schedule = ""; diff --git a/restore.js b/restore.js index f7f6e06..637fe41 100644 --- a/restore.js +++ b/restore.js @@ -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); }); } diff --git a/routes.js b/routes.js index 7f47589..7256561 100644 --- a/routes.js +++ b/routes.js @@ -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 } diff --git a/views/index.ejs b/views/index.ejs index 3cbcbbf..0a365a7 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -20,14 +20,29 @@ <% include navbar %>

Cronjobs

+ + New + Backup + + Import + Export + + Save to crontab +
+
+ + + <% var index = 1 %> <% JSON.parse(crontabs).forEach(function(crontab){ %> <% if (!crontab.stopped) { %> @@ -35,6 +50,11 @@ <% } else { %> <% } %> + - +
Id Job Time Last Modified
+ <%= index %>. + <% index += 1 %> + <%= crontab._id %> + <% if (crontab.name) { %> @@ -42,7 +62,7 @@ <% } %> <%= crontab.command %><%= crontab.schedule %><%= crontab.schedule %> <%= crontab.timestamp %> @@ -62,11 +82,6 @@
- New - Backup - Export - Import from crontab - Save to crontab
<% include popup.ejs %> diff --git a/views/navbar.ejs b/views/navbar.ejs index dedbe9f..e653059 100644 --- a/views/navbar.ejs +++ b/views/navbar.ejs @@ -14,7 +14,6 @@