Merge remote-tracking branch 'upstream/master'

pull/93/head
ntsd 2020-06-17 22:44:18 +07:00
commit 0b437e1555
15 changed files with 530 additions and 308 deletions

View File

@ -1,2 +1,5 @@
.git
*.swp
node_modules node_modules
.git crontabs/*.db
crontabs/logs/*.log

View File

@ -1,7 +1,9 @@
# docker run -d -p 8000:8000 alseambusher/crontab-ui # docker run -d -p 8000:8000 alseambusher/crontab-ui
FROM alpine:3.5 FROM alpine:3.10
RUN mkdir /crontab-ui; touch /etc/crontabs/root; chmod +x /etc/crontabs/root ENV CRON_PATH /etc/crontabs
RUN mkdir /crontab-ui; touch $CRON_PATH/root; chmod +x $CRON_PATH/root
WORKDIR /crontab-ui WORKDIR /crontab-ui
@ -12,6 +14,7 @@ RUN apk --no-cache add \
wget \ wget \
curl \ curl \
nodejs \ nodejs \
npm \
supervisor supervisor
COPY supervisord.conf /etc/supervisord.conf COPY supervisord.conf /etc/supervisord.conf
@ -23,7 +26,6 @@ ENV HOST 0.0.0.0
ENV PORT 8000 ENV PORT 8000
ENV CRON_PATH /etc/crontabs
ENV CRON_IN_DOCKER true ENV CRON_IN_DOCKER true
EXPOSE $PORT EXPOSE $PORT

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
VER=0.3.12
release:
sed -i '' "s/version\": \".*/version\": \"$(VER)\",/" package.json
npm publish
docker build -t alseambusher/crontab-ui .
docker tag alseambusher/crontab-ui alseambusher/crontab-ui:$(VER)
docker push alseambusher/crontab-ui:latest
docker push alseambusher/crontab-ui:$(VER)

View File

@ -2,9 +2,10 @@ Crontab UI
========== ==========
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=U8328Q7VFZMTS) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=U8328Q7VFZMTS)
[![npm](https://img.shields.io/npm/v/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html)
[![npm](https://img.shields.io/npm/dt/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html) [![npm](https://img.shields.io/npm/dt/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html)
[![npm](https://img.shields.io/npm/dm/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html) [![npm](https://img.shields.io/npm/dm/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html)
[![npm](https://img.shields.io/npm/v/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html) [![npm](https://img.shields.io/docker/pulls/alseambusher/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html)
[![npm](https://img.shields.io/npm/l/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html) [![npm](https://img.shields.io/npm/l/crontab-ui.svg?style=flat-square)](http://lifepluslinux.blogspot.in/2015/06/crontab-ui-easy-and-safe-way-to-manage.html)
Editing the plain text crontab is error prone for managing jobs, e.g., adding jobs, deleting jobs, or pausing jobs. A small mistake can easily bring down all the jobs and might cost you a lot of time. With Crontab UI, it is very easy to manage crontab. Here are the key features of Crontab UI. Editing the plain text crontab is error prone for managing jobs, e.g., adding jobs, deleting jobs, or pausing jobs. A small mistake can easily bring down all the jobs and might cost you a lot of time. With Crontab UI, it is very easy to manage crontab. Here are the key features of Crontab UI.
@ -31,20 +32,40 @@ If you need to set/use an alternative host or port, you may do so by setting an
HOST=0.0.0.0 PORT=9000 crontab-ui HOST=0.0.0.0 PORT=9000 crontab-ui
If you need to apply basic HTTP authentication, you can set user name and password through environment variables:
BASIC_AUTH_USER=user BASIC_AUTH_PWD=SecretPassword
Also, you may have to **set permissions** for your `node_modules` folder. Refer [this](https://docs.npmjs.com/getting-started/fixing-npm-permissions). Also, you may have to **set permissions** for your `node_modules` folder. Refer [this](https://docs.npmjs.com/getting-started/fixing-npm-permissions).
If you need to autosave your changes to crontab directly: If you need to autosave your changes to crontab directly:
crontab-ui --autosave crontab-ui --autosave
Setting up with docker: ## Docker
You can use crontab-ui with docker. You can use the prebuilt images in the [dockerhub](https://hub.docker.com/r/alseambusher/crontab-ui/tags)
```bash
docker run -d -p 8000:8000 alseambusher/crontab-ui
```
You can also build it yourself if you want to customize, like this:
```bash ```bash
git clone https://github.com/alseambusher/crontab-ui.git git clone https://github.com/alseambusher/crontab-ui.git
cd crontab-ui cd crontab-ui
docker build -t alseambusher/crontab-ui . docker build -t alseambusher/crontab-ui .
docker run -d -p 8000:8000 alseambusher/crontab-ui docker run -d -p 8000:8000 alseambusher/crontab-ui
``` ```
If you want to use it with authentication, You can pass `BASIC_AUTH_USER` and `BASIC_AUTH_PWD` as env variables
```bash
docker run -e BASIC_AUTH_USER=user -e BASIC_AUTH_PWD=SecretPassword -d -p 8000:8000 alseambusher/crontab-ui
```
You can also mount a folder to store the db and logs.
```bash
mkdir -p crontabs/logs
docker run --mount type=bind,source="$(pwd)"/crontabs/,target=/crontab-ui/crontabs/ -d -p 8000:8000 alseambusher/crontab-ui
```
## Resources ## Resources

View File

@ -22,5 +22,11 @@ server {
Authentication Authentication
============== ==============
You can enable basic http authentication with user name and password on nginx to prevent unauthorized users from accessing your cronjobs. Refer [this](https://www.digitalocean.com/community/tutorials/how-to-set-up-http-authentication-with-nginx-on-ubuntu-12-10). If you need to apply basic HTTP authentication, you can set user name and password through environment variables:
```
BASIC_AUTH_USER=user BASIC_AUTH_PWD=SecretPassword
```
You can also enable basic http authentication with user name and password on nginx to prevent unauthorized users from accessing your cronjobs. Refer [this](https://www.digitalocean.com/community/tutorials/how-to-set-up-http-authentication-with-nginx-on-ubuntu-12-10).

59
app.js
View File

@ -4,14 +4,33 @@ var app = express();
var crontab = require("./crontab"); var crontab = require("./crontab");
var restore = require("./restore"); var restore = require("./restore");
var moment = require('moment'); var moment = require('moment');
var basicAuth = require('express-basic-auth');
var path = require('path'); var path = require('path');
var mime = require('mime-types'); var mime = require('mime-types');
var fs = require('fs'); var fs = require('fs');
var busboy = require('connect-busboy'); // for file upload var busboy = require('connect-busboy'); // for file upload
// basic auth
var BASIC_AUTH_USER = process.env.BASIC_AUTH_USER;
var BASIC_AUTH_PWD = process.env.BASIC_AUTH_PWD;
if (BASIC_AUTH_USER && BASIC_AUTH_PWD) {
app.use(function(req, res, next) {
res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"')
next();
});
app.use(basicAuth({
users: {
[BASIC_AUTH_USER]: BASIC_AUTH_PWD
}
}))
}
// include the routes // include the routes
var routes = require("./routes").routes; var routes = require("./routes").routes;
var routes_relative = require("./routes").relative
// set the view engine to ejs // set the view engine to ejs
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
@ -43,7 +62,7 @@ app.get(routes.root, function(req, res) {
// send all the required parameters // send all the required parameters
crontab.crontabs( function(docs){ crontab.crontabs( function(docs){
res.render('index', { res.render('index', {
routes : JSON.stringify(routes), routes : JSON.stringify(routes_relative),
crontabs : JSON.stringify(docs), crontabs : JSON.stringify(docs),
backups : crontab.get_backup_names(), backups : crontab.get_backup_names(),
env : crontab.get_env(), env : crontab.get_env(),
@ -112,7 +131,7 @@ app.get(routes.restore, function(req, res) {
// get all the crontabs // get all the crontabs
restore.crontabs(req.query.db, function(docs){ restore.crontabs(req.query.db, function(docs){
res.render('restore', { res.render('restore', {
routes : JSON.stringify(routes), routes : JSON.stringify(routes_relative),
crontabs : JSON.stringify(docs), crontabs : JSON.stringify(docs),
backups : crontab.get_backup_names(), backups : crontab.get_backup_names(),
db: req.query.db db: req.query.db
@ -134,7 +153,7 @@ app.get(routes.restore_backup, function(req, res) {
// export current crontab db so that user can download it // export current crontab db so that user can download it
app.get(routes.export, function(req, res) { app.get(routes.export, function(req, res) {
var file = __dirname + '/crontabs/crontab.db'; var file = crontab.crontab_db_file;
var filename = path.basename(file); var filename = path.basename(file);
var mimetype = mime.lookup(file); var mimetype = mime.lookup(file);
@ -151,7 +170,7 @@ app.post(routes.import, function(req, res) {
var fstream; var fstream;
req.pipe(req.busboy); req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) { req.busboy.on('file', function (fieldname, file, filename) {
fstream = fs.createWriteStream(__dirname + '/crontabs/crontab.db'); fstream = fs.createWriteStream(crontab.crontab_db_file);
file.pipe(fstream); file.pipe(fstream);
fstream.on('close', function () { fstream.on('close', function () {
crontab.reload_db(); crontab.reload_db();
@ -166,13 +185,23 @@ app.get(routes.import_crontab, function(req, res) {
res.end(); res.end();
}); });
// get the log file a given job. id passed as query param function sendLog(path, req, res) {
app.get(routes.logger, function(req, res) { if (fs.existsSync(path))
_file = crontab.log_folder +"/"+req.query.id+".log"; res.sendFile(path);
if (fs.existsSync(_file))
res.sendFile(_file);
else else
res.end("No errors logged yet"); res.end("No errors logged yet");
}
// get the log file a given job. id passed as query param
app.get(routes.logger, function(req, res) {
let _file = crontab.log_folder + "/" + req.query.id + ".log";
sendLog(_file, req, res);
});
// get the log file a given job. id passed as query param
app.get(routes.stdout, function(req, res) {
let _file = crontab.log_folder + "/" + req.query.id + ".stdout.log";
sendLog(_file, req, res);
}); });
// error handler // error handler
@ -186,7 +215,7 @@ app.use(function(err, req, res, next) {
data.stack = err.stack; data.stack = err.stack;
} }
if (parseInt(data.statusCode) >= 500) { if (statusCode >= 500) {
console.error(err); console.error(err);
} }
@ -205,9 +234,9 @@ process.on('SIGTERM', function() {
app.listen(app.get('port'), app.get('host'), function() { app.listen(app.get('port'), app.get('host'), function() {
console.log("Node version:", process.versions.node); console.log("Node version:", process.versions.node);
fs.access(__dirname + "/crontabs/", fs.W_OK, function(err) { fs.access(crontab.db_folder, fs.W_OK, function(err) {
if(err){ if(err){
console.error("Write access to", __dirname + "/crontabs/", "DENIED."); console.error("Write access to", crontab.db_folder, "DENIED.");
process.exit(1); process.exit(1);
} }
}); });
@ -215,7 +244,7 @@ app.listen(app.get('port'), app.get('host'), function() {
// we do this by watching log file and setting a on change hook to it // we do this by watching log file and setting a on change hook to it
if (process.argv.includes("--autosave")){ if (process.argv.includes("--autosave")){
crontab.autosave_crontab(()=>{}); crontab.autosave_crontab(()=>{});
fs.watchFile(__dirname + '/crontabs/crontab.db', () => { fs.watchFile(crontab.crontab_db_file, () => {
crontab.autosave_crontab(()=>{ crontab.autosave_crontab(()=>{
console.log("Attempted to autosave crontab"); console.log("Attempted to autosave crontab");
}); });
@ -223,8 +252,8 @@ app.listen(app.get('port'), app.get('host'), function() {
} }
if (process.argv.includes("--reset")){ if (process.argv.includes("--reset")){
console.log("Resetting crontab-ui"); console.log("Resetting crontab-ui");
var crontabdb = __dirname + "/crontabs/crontab.db"; var crontabdb = crontab.crontab_db_file;
var envdb = __dirname + "/crontabs/env.db"; var envdb = crontab.env_file;
console.log("Deleting " + crontabdb); console.log("Deleting " + crontabdb);
try{ try{

View File

@ -2,7 +2,14 @@
//load database //load database
var Datastore = require('nedb'); var Datastore = require('nedb');
var path = require("path"); var path = require("path");
var db = new Datastore({ filename: __dirname + '/crontabs/crontab.db' });
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');
var db = new Datastore({ filename: exports.crontab_db_file});
var cronPath = "/tmp"; var cronPath = "/tmp";
if(process.env.CRON_PATH !== undefined) { if(process.env.CRON_PATH !== undefined) {
@ -18,8 +25,6 @@ var exec = require('child_process').exec;
var fs = require('fs'); var fs = require('fs');
var cron_parser = require("cron-parser"); var cron_parser = require("cron-parser");
exports.log_folder = __dirname + '/crontabs/logs';
exports.env_file = __dirname + '/crontabs/env.db';
crontab = function(name, command, schedule, stopped, logging, mailing){ crontab = function(name, command, schedule, stopped, logging, mailing){
var data = {}; var data = {};
@ -40,15 +45,18 @@ crontab = function(name, command, schedule, stopped, logging, mailing){
exports.create_new = function(name, command, schedule, logging, mailing){ exports.create_new = function(name, command, schedule, logging, mailing){
var tab = crontab(name, command, schedule, false, logging, mailing); var tab = crontab(name, command, schedule, false, logging, mailing);
tab.created = new Date().valueOf(); tab.created = new Date().valueOf();
tab.saved = false;
db.insert(tab); db.insert(tab);
}; };
exports.update = function(data){ exports.update = function(data){
db.update({_id: data._id}, crontab(data.name, data.command, data.schedule, null, data.logging, data.mailing)); var tab = crontab(data.name, data.command, data.schedule, null, data.logging, data.mailing);
tab.saved = false;
db.update({_id: data._id}, tab);
}; };
exports.status = function(_id, stopped){ exports.status = function(_id, stopped){
db.update({_id: _id},{$set: {stopped: stopped}}); db.update({_id: _id},{$set: {stopped: stopped, saved: false}});
}; };
exports.remove = function(_id){ exports.remove = function(_id){
@ -62,7 +70,12 @@ exports.crontabs = function(callback){
if(docs[i].schedule == "@reboot") if(docs[i].schedule == "@reboot")
docs[i].next = "Next Reboot"; docs[i].next = "Next Reboot";
else else
docs[i].next = cron_parser.parseExpression(docs[i].schedule).next().toString(); try {
docs[i].next = cron_parser.parseExpression(docs[i].schedule).next().toString();
} catch(err) {
console.error(err);
docs[i].next = "invalid";
}
} }
callback(docs); callback(docs);
}); });
@ -95,6 +108,7 @@ exports.set_crontab = function(env_vars, callback){
let stderr = path.join(cronPath, tab._id + ".stderr"); let stderr = path.join(cronPath, tab._id + ".stderr");
let stdout = path.join(cronPath, tab._id + ".stdout"); let stdout = path.join(cronPath, tab._id + ".stdout");
let log_file = path.join(exports.log_folder, tab._id + ".log"); let log_file = path.join(exports.log_folder, tab._id + ".log");
let log_file_stdout = path.join(exports.log_folder, tab._id + ".stdout.log");
if(tab.command[tab.command.length-1] != ";") // add semicolon if(tab.command[tab.command.length-1] != ";") // add semicolon
tab.command +=";"; tab.command +=";";
@ -103,8 +117,13 @@ exports.set_crontab = function(env_vars, callback){
if (tab.logging && tab.logging == "true") { if (tab.logging && tab.logging == "true") {
crontab_string += "; if test -f " + stderr + crontab_string += "; if test -f " + stderr +
"; then date >> " + log_file + "; then date >> \"" + log_file + "\"" +
"; cat " + stderr + " >> " + log_file + "; cat " + stderr + " >> \"" + log_file + "\"" +
"; fi";
crontab_string += "; if test -f " + stdout +
"; then date >> \"" + log_file_stdout + "\"" +
"; cat " + stdout + " >> \"" + log_file_stdout + "\"" +
"; fi"; "; fi";
} }
@ -123,24 +142,28 @@ exports.set_crontab = function(env_vars, callback){
}); });
fs.writeFile(exports.env_file, env_vars, function(err) { fs.writeFile(exports.env_file, env_vars, function(err) {
if (err) callback(err); if (err) {
// In docker we're running as the root user, so we need to write the file as root and not crontab console.error(err);
var fileName = "crontab" callback(err);
if(process.env.CRON_IN_DOCKER !== undefined) {
fileName = "root"
} }
// 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) { fs.writeFile(path.join(cronPath, fileName), crontab_string, function(err) {
if (err) return callback(err); if (err) {
/// In docker we're running crond using busybox implementation of crond console.error(err);
/// It is launched as part of the container startup process, so no need to run it again return callback(err);
if(process.env.CRON_IN_DOCKER === undefined) {
exec("crontab " + path.join(cronPath, "crontab"), function(err) {
if (err) return callback(err);
else callback();
});
} else {
callback();
} }
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();
}
});
}); });
}); });
}); });
@ -148,7 +171,7 @@ exports.set_crontab = function(env_vars, callback){
exports.get_backup_names = function(){ exports.get_backup_names = function(){
var backups = []; var backups = [];
fs.readdirSync(__dirname + '/crontabs').forEach(function(file){ fs.readdirSync(exports.db_folder).forEach(function(file){
// file name begins with backup // file name begins with backup
if(file.indexOf("backup") === 0){ if(file.indexOf("backup") === 0){
backups.push(file); backups.push(file);
@ -175,11 +198,11 @@ exports.get_backup_names = function(){
exports.backup = function(){ exports.backup = function(){
//TODO check if it failed //TODO check if it failed
fs.createReadStream( __dirname + '/crontabs/crontab.db').pipe(fs.createWriteStream( __dirname + '/crontabs/backup ' + (new Date()).toString().replace("+", " ") + '.db')); fs.createReadStream(exports.crontab_db_file).pipe(fs.createWriteStream( path.join(exports.db_folder, 'backup ' + (new Date()).toString().replace("+", " ") + '.db')));
}; };
exports.restore = function(db_name){ exports.restore = function(db_name){
fs.createReadStream( __dirname + '/crontabs/' + db_name).pipe(fs.createWriteStream( __dirname + '/crontabs/crontab.db')); fs.createReadStream(path.join(exports.db_folder, db_name)).pipe(fs.createWriteStream(exports.crontab_db_file));
db.loadDatabase(); // reload the database db.loadDatabase(); // reload the database
}; };

9
docker-compose.yml Normal file
View File

@ -0,0 +1,9 @@
version: '3.7'
services:
crontab-ui:
build: .
image: alseambusher/crontab-ui
network_mode: bridge
ports:
- 8000:8000

581
package-lock.json generated
View File

@ -1,16 +1,24 @@
{ {
"name": "crontab-ui", "name": "crontab-ui",
"version": "0.3.4", "version": "0.3.9",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"accepts": { "accepts": {
"version": "1.3.5", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": { "requires": {
"mime-types": "2.1.18", "mime-types": "~2.1.24",
"negotiator": "0.6.1" "negotiator": "0.6.2"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
} }
}, },
"array-flatten": { "array-flatten": {
@ -19,61 +27,113 @@
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
}, },
"async": { "async": {
"version": "0.2.10", "version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
}
}, },
"binary-search-tree": { "binary-search-tree": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz",
"integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=",
"requires": { "requires": {
"underscore": "1.4.4" "underscore": "~1.4.4"
} }
}, },
"body-parser": { "body-parser": {
"version": "1.18.2", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": { "requires": {
"bytes": "3.0.0", "bytes": "3.1.0",
"content-type": "1.0.4", "content-type": "~1.0.4",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.2", "depd": "~1.1.2",
"http-errors": "1.6.2", "http-errors": "1.7.2",
"iconv-lite": "0.4.19", "iconv-lite": "0.4.24",
"on-finished": "2.3.0", "on-finished": "~2.3.0",
"qs": "6.5.1", "qs": "6.7.0",
"raw-body": "2.3.2", "raw-body": "2.4.0",
"type-is": "1.6.16" "type-is": "~1.6.17"
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
} }
}, },
"busboy": { "busboy": {
"version": "0.2.14", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz",
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==",
"requires": { "requires": {
"dicer": "0.2.5", "dicer": "0.3.0"
"readable-stream": "1.1.14"
} }
}, },
"bytes": { "bytes": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
}, },
"connect-busboy": { "connect-busboy": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/connect-busboy/-/connect-busboy-0.0.2.tgz", "resolved": "https://registry.npmjs.org/connect-busboy/-/connect-busboy-0.0.2.tgz",
"integrity": "sha1-rFyclmchcYheV2xmsr/ZXTuxEJc=", "integrity": "sha1-rFyclmchcYheV2xmsr/ZXTuxEJc=",
"requires": { "requires": {
"busboy": "0.2.14" "busboy": "*"
} }
}, },
"content-disposition": { "content-disposition": {
"version": "0.5.2", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
}, },
"content-type": { "content-type": {
"version": "1.0.4", "version": "1.0.4",
@ -81,27 +141,22 @@
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
}, },
"cookie": { "cookie": {
"version": "0.3.1", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
}, },
"cookie-signature": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}, },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cron-parser": { "cron-parser": {
"version": "2.4.5", "version": "2.15.0",
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.4.5.tgz", "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.15.0.tgz",
"integrity": "sha512-J/BXGGFLQCxrLqcGT9Zp1Yz9H1LfZqlUJ7JRgfbtwt6fkWGVKKtMskF/iDxkA7MXJoaYerwyHX48lZuZoOJ1Eg==", "integrity": "sha512-rMFkrQw8+oG5OuwjiXesup4KeIlEG/IU82YtG4xyAHbO5jhKmYaHPp/ZNhq9+7TjSJ65E3zV3kQPUbmXSff2/g==",
"requires": { "requires": {
"is-nan": "1.2.1", "is-nan": "^1.3.0",
"moment-timezone": "0.5.14" "moment-timezone": "^0.5.31"
} }
}, },
"debug": { "debug": {
@ -113,12 +168,11 @@
} }
}, },
"define-properties": { "define-properties": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"requires": { "requires": {
"foreach": "2.0.5", "object-keys": "^1.0.12"
"object-keys": "1.0.11"
} }
}, },
"depd": { "depd": {
@ -132,11 +186,10 @@
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
}, },
"dicer": { "dicer": {
"version": "0.2.5", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
"requires": { "requires": {
"readable-stream": "1.1.14",
"streamsearch": "0.1.2" "streamsearch": "0.1.2"
} }
}, },
@ -146,9 +199,12 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
}, },
"ejs": { "ejs": {
"version": "2.5.7", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz",
"integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==",
"requires": {
"jake": "^10.6.1"
}
}, },
"encodeurl": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
@ -160,74 +216,83 @@
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
}, },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
}, },
"express": { "express": {
"version": "4.16.3", "version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": { "requires": {
"accepts": "1.3.5", "accepts": "~1.3.7",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"body-parser": "1.18.2", "body-parser": "1.19.0",
"content-disposition": "0.5.2", "content-disposition": "0.5.3",
"content-type": "1.0.4", "content-type": "~1.0.4",
"cookie": "0.3.1", "cookie": "0.4.0",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.2", "depd": "~1.1.2",
"encodeurl": "1.0.2", "encodeurl": "~1.0.2",
"escape-html": "1.0.3", "escape-html": "~1.0.3",
"etag": "1.8.1", "etag": "~1.8.1",
"finalhandler": "1.1.1", "finalhandler": "~1.1.2",
"fresh": "0.5.2", "fresh": "0.5.2",
"merge-descriptors": "1.0.1", "merge-descriptors": "1.0.1",
"methods": "1.1.2", "methods": "~1.1.2",
"on-finished": "2.3.0", "on-finished": "~2.3.0",
"parseurl": "1.3.2", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.7", "path-to-regexp": "0.1.7",
"proxy-addr": "2.0.3", "proxy-addr": "~2.0.5",
"qs": "6.5.1", "qs": "6.7.0",
"range-parser": "1.2.0", "range-parser": "~1.2.1",
"safe-buffer": "5.1.1", "safe-buffer": "5.1.2",
"send": "0.16.2", "send": "0.17.1",
"serve-static": "1.13.2", "serve-static": "1.14.1",
"setprototypeof": "1.1.0", "setprototypeof": "1.1.1",
"statuses": "1.4.0", "statuses": "~1.5.0",
"type-is": "1.6.16", "type-is": "~1.6.18",
"utils-merge": "1.0.1", "utils-merge": "1.0.1",
"vary": "1.1.2" "vary": "~1.1.2"
}, }
"dependencies": { },
"setprototypeof": { "express-basic-auth": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" "integrity": "sha512-iJ0h1Gk6fZRrFmO7tP9nIbxwNgCUJASfNj5fb0Hy15lGtbqqsxpt7609+wq+0XlByZjXmC/rslWQtnuSTVRIcg==",
} "requires": {
"basic-auth": "^2.0.1"
}
},
"filelist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz",
"integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==",
"requires": {
"minimatch": "^3.0.4"
} }
}, },
"finalhandler": { "finalhandler": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
"encodeurl": "1.0.2", "encodeurl": "~1.0.2",
"escape-html": "1.0.3", "escape-html": "~1.0.3",
"on-finished": "2.3.0", "on-finished": "~2.3.0",
"parseurl": "1.3.2", "parseurl": "~1.3.3",
"statuses": "1.4.0", "statuses": "~1.5.0",
"unpipe": "1.0.0" "unpipe": "~1.0.0"
} }
}, },
"foreach": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -238,28 +303,30 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
}, },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"http-errors": { "http-errors": {
"version": "1.6.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": { "requires": {
"depd": "1.1.1", "depd": "~1.1.2",
"inherits": "2.0.3", "inherits": "2.0.3",
"setprototypeof": "1.0.3", "setprototypeof": "1.1.1",
"statuses": "1.4.0" "statuses": ">= 1.5.0 < 2",
}, "toidentifier": "1.0.0"
"dependencies": {
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
}
} }
}, },
"iconv-lite": { "iconv-lite": {
"version": "0.4.19", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}, },
"immediate": { "immediate": {
"version": "3.0.6", "version": "3.0.6",
@ -272,35 +339,41 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}, },
"ipaddr.js": { "ipaddr.js": {
"version": "1.6.0", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}, },
"is-nan": { "is-nan": {
"version": "1.2.1", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz",
"integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==",
"requires": { "requires": {
"define-properties": "1.1.2" "define-properties": "^1.1.3"
} }
}, },
"isarray": { "jake": {
"version": "0.0.1", "version": "10.8.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" "integrity": "sha512-eSp5h9S7UFzKdQERTyF+KuPLjDZa1Tbw8gCVUn98n4PbIkLEDGe4zl7vF4Qge9kQj06HcymnksPk8jznPZeKsA==",
"requires": {
"async": "0.9.x",
"chalk": "^2.4.2",
"filelist": "^1.0.1",
"minimatch": "^3.0.4"
}
}, },
"lie": { "lie": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
"requires": { "requires": {
"immediate": "3.0.6" "immediate": "~3.0.5"
} }
}, },
"localforage": { "localforage": {
"version": "1.7.2", "version": "1.7.4",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.2.tgz", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.4.tgz",
"integrity": "sha1-+kRCYC+Abt0rympUq05lbwMfEhw=", "integrity": "sha512-3EmVZatmNVeCo/t6Te7P06h2alGwbq8wXlSkcSXMvDE2/edPmsVqTPlzGnZaqwZZDBs6v+kxWpqjVsqsNJT8jA==",
"requires": { "requires": {
"lie": "3.1.1" "lie": "3.1.1"
} }
@ -321,47 +394,55 @@
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
}, },
"mime": { "mime": {
"version": "1.4.1", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
}, },
"mime-db": { "mime-db": {
"version": "1.33.0", "version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
}, },
"mime-types": { "mime-types": {
"version": "2.1.18", "version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": { "requires": {
"mime-db": "1.33.0" "mime-db": "1.44.0"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "^1.2.5"
} }
}, },
"moment": { "moment": {
"version": "2.21.0", "version": "2.26.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
"integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
}, },
"moment-timezone": { "moment-timezone": {
"version": "0.5.14", "version": "0.5.31",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz", "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
"integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=", "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
"requires": { "requires": {
"moment": "2.21.0" "moment": ">= 2.9.0"
} }
}, },
"ms": { "ms": {
@ -376,25 +457,32 @@
"requires": { "requires": {
"async": "0.2.10", "async": "0.2.10",
"binary-search-tree": "0.2.5", "binary-search-tree": "0.2.5",
"localforage": "1.7.2", "localforage": "^1.3.0",
"mkdirp": "0.5.1", "mkdirp": "~0.5.1",
"underscore": "1.4.4" "underscore": "~1.4.4"
},
"dependencies": {
"async": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
}
} }
}, },
"negotiator": { "negotiator": {
"version": "0.6.1", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"nodemailer": { "nodemailer": {
"version": "4.6.3", "version": "6.4.8",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.3.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.8.tgz",
"integrity": "sha512-1AmOpDZJtyPAO+gfUBfT+MWHbYwQ+DZvb1gvYaTxBZV/lUeysZIt4kDq8Dlwt6ViUZGp3cMGR+D1MNQYyYiVUg==" "integrity": "sha512-UbJD0+g5e2H20bWv7Rpj3B+N3TMMJ0MLoLwaGVJ0k3Vo8upq0UltwHJ5BJfrpST1vFa91JQ8cf7cICK5DSIo1Q=="
}, },
"object-keys": { "object-keys": {
"version": "1.0.11", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
}, },
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
@ -405,9 +493,9 @@
} }
}, },
"parseurl": { "parseurl": {
"version": "1.3.2", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
}, },
"path-to-regexp": { "path-to-regexp": {
"version": "0.1.7", "version": "0.1.7",
@ -415,109 +503,118 @@
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
}, },
"proxy-addr": { "proxy-addr": {
"version": "2.0.3", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": { "requires": {
"forwarded": "0.1.2", "forwarded": "~0.1.2",
"ipaddr.js": "1.6.0" "ipaddr.js": "1.9.1"
} }
}, },
"qs": { "qs": {
"version": "6.5.1", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
}, },
"range-parser": { "range-parser": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
}, },
"raw-body": { "raw-body": {
"version": "2.3.2", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": { "requires": {
"bytes": "3.0.0", "bytes": "3.1.0",
"http-errors": "1.6.2", "http-errors": "1.7.2",
"iconv-lite": "0.4.19", "iconv-lite": "0.4.24",
"unpipe": "1.0.0" "unpipe": "1.0.0"
} }
}, },
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
}
},
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"send": { "send": {
"version": "0.16.2", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.2", "depd": "~1.1.2",
"destroy": "1.0.4", "destroy": "~1.0.4",
"encodeurl": "1.0.2", "encodeurl": "~1.0.2",
"escape-html": "1.0.3", "escape-html": "~1.0.3",
"etag": "1.8.1", "etag": "~1.8.1",
"fresh": "0.5.2", "fresh": "0.5.2",
"http-errors": "1.6.2", "http-errors": "~1.7.2",
"mime": "1.4.1", "mime": "1.6.0",
"ms": "2.0.0", "ms": "2.1.1",
"on-finished": "2.3.0", "on-finished": "~2.3.0",
"range-parser": "1.2.0", "range-parser": "~1.2.1",
"statuses": "1.4.0" "statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
} }
}, },
"serve-static": { "serve-static": {
"version": "1.13.2", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": { "requires": {
"encodeurl": "1.0.2", "encodeurl": "~1.0.2",
"escape-html": "1.0.3", "escape-html": "~1.0.3",
"parseurl": "1.3.2", "parseurl": "~1.3.3",
"send": "0.16.2" "send": "0.17.1"
} }
}, },
"setprototypeof": { "setprototypeof": {
"version": "1.0.3", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
}, },
"statuses": { "statuses": {
"version": "1.4.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
}, },
"streamsearch": { "streamsearch": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
}, },
"string_decoder": { "supports-color": {
"version": "0.10.31", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
}, },
"type-is": { "type-is": {
"version": "1.6.16", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": { "requires": {
"media-typer": "0.3.0", "media-typer": "0.3.0",
"mime-types": "2.1.18" "mime-types": "~2.1.24"
} }
}, },
"underscore": { "underscore": {

View File

@ -1,20 +1,21 @@
{ {
"name": "crontab-ui", "name": "crontab-ui",
"version": "0.3.5", "version": "0.3.12",
"description": "Easy and safe way to manage your crontab file", "description": "Easy and safe way to manage your crontab file",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node app.js" "start": "node app.js"
}, },
"dependencies": { "dependencies": {
"express": "latest",
"ejs": "latest",
"nedb": "latest",
"body-parser": "latest", "body-parser": "latest",
"mime-types": "latest",
"cron-parser": "latest",
"connect-busboy": "latest", "connect-busboy": "latest",
"cron-parser": "latest",
"ejs": "latest",
"express": "latest",
"express-basic-auth": "^1.2.0",
"mime-types": "latest",
"moment": "latest", "moment": "latest",
"nedb": "latest",
"nodemailer": "latest" "nodemailer": "latest"
}, },
"engines": { "engines": {

View File

@ -69,6 +69,7 @@ function setCrontab(){
$.get(routes.crontab, { "env_vars": $("#env_vars").val() }, function(){ $.get(routes.crontab, { "env_vars": $("#env_vars").val() }, function(){
// TODO show only if success // TODO show only if success
infoMessageBox("Successfuly set crontab file!","Information"); infoMessageBox("Successfuly set crontab file!","Information");
location.reload();
}).fail(function(response) { }).fail(function(response) {
errorMessageBox(response.statusText,"Error"); errorMessageBox(response.statusText,"Error");
}); });

View File

@ -1,11 +1,13 @@
//load database //load database
var Datastore = require('nedb'); var Datastore = require('nedb');
var crontab = require("./crontab");
var path = require("path");
var exec = require('child_process').exec; var exec = require('child_process').exec;
var fs = require('fs'); var fs = require('fs');
exports.crontabs = function(db_name, callback){ exports.crontabs = function(db_name, callback){
var db = new Datastore({ filename: __dirname + '/crontabs/' + db_name }); var db = new Datastore({filename: path.join(crontab.db_folder, db_name)});
db.loadDatabase(function (err) { db.loadDatabase(function (err) {
}); });
db.find({}).sort({ created: -1 }).exec(function(err, docs){ db.find({}).sort({ created: -1 }).exec(function(err, docs){
@ -14,5 +16,12 @@ exports.crontabs = function(db_name, callback){
}; };
exports.delete = function(db_name){ exports.delete = function(db_name){
fs.unlink(__dirname + '/crontabs/' + db_name); fs.unlink(path.join(crontab.db_folder, db_name), function(err){
if(err) {
return console.log("Delete error: " + err);
}
else{
console.log("Backup deleted");
}
});
}; };

View File

@ -14,4 +14,8 @@ exports.routes = {
"import": "/import", // this is import from database "import": "/import", // this is import from database
"import_crontab": "/import_crontab", // this is from existing crontab "import_crontab": "/import_crontab", // this is from existing crontab
"logger": "/logger", "logger": "/logger",
"stdout": "/stdout",
}; };
exports.relative = Object.keys(exports.routes).reduce((p, c) => ({...p, [c]: exports.routes[c].replace(/^\//, '')}), {});
exports.relative["root"] = ".";

View File

@ -21,8 +21,8 @@
</script> </script>
</head> </head>
<body> <body>
<% include navbar %> <%- include('navbar.ejs') -%>
<div class="container"> <div class="container-fluid">
<h2>Cronjobs</h2> <h2>Cronjobs</h2>
<div class="form-group"> <div class="form-group">
<label for="env_vars">Environment Variables:</label> <label for="env_vars">Environment Variables:</label>
@ -70,8 +70,15 @@
<% if (crontab.name) { %> <% if (crontab.name) { %>
<%= crontab.name %> <%= crontab.name %>
<a class="btn" data-toggle="tooltip" data-placement="right" title="<%= crontab._id %>"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> </a> <a class="btn" data-toggle="tooltip" data-placement="right" title="<%= crontab._id %>"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> </a>
<% if (crontab.saved) { %>
<span class="glyphicon glyphicon-floppy-saved" aria-hidden="true"></span>
<% } else { %> <% } else { %>
<%= crontab._id %> <a data-toggle="tooltip" data-placement="right" title="'Save to crontab' to deploy">
<span class="glyphicon glyphicon-floppy-remove" aria-hidden="true"></span>
</a>
<% } %>
<% } else { %>
<%= crontab._id %>
<% } %> <% } %>
</td> </td>
<td><%= crontab.command %></td> <td><%= crontab.command %></td>
@ -82,13 +89,14 @@
<!-- controls based on crontab state --> <!-- controls based on crontab state -->
<% if (!crontab.stopped) { %> <% if (!crontab.stopped) { %>
<% if (crontab.logging && crontab.logging != "false") {%> <% if (crontab.logging && crontab.logging != "false") {%>
<a class="btn btn-primary" data-toggle="tooltip" data-placement="left" title="See Log" href="<%= JSON.parse(routes).logger + '?id=' + crontab._id %>" target="_blank"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span></a> <a class="btn btn-primary btn-danger" data-toggle="tooltip" data-placement="left" title="stderr" href="<%= JSON.parse(routes).logger + '?id=' + crontab._id %>" target="_blank"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span></a>
<a class="btn btn-primary" data-toggle="tooltip" data-placement="left" title="stdout" href="<%= JSON.parse(routes).stdout + '?id=' + crontab._id %>" target="_blank"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span></a>
<% } %> <% } %>
<a class="btn btn-info" onclick="runJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Run</a> <a class="btn btn-info" onclick="runJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Run now</a>
<a class="btn btn-primary" onclick="editJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit</a> <a class="btn btn-primary" onclick="editJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit</a>
<a class="btn btn-info" onclick="stopJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-stop" aria-hidden="true"></span> Stop</a> <a class="btn btn-info" onclick="stopJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-stop" aria-hidden="true"></span> Disable</a>
<% } else { %> <% } else { %>
<a class="btn btn-info" onclick="startJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Start</a> <a class="btn btn-info" onclick="startJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Enable</a>
<% } %> <% } %>
<a class="btn btn-danger" onclick="deleteJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a> <a class="btn btn-danger" onclick="deleteJob('<%= crontab._id %>')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
</td> </td>
@ -99,7 +107,7 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<% include popup.ejs %> <%- include('popup.ejs') -%>
<script> <script>
jQuery(function($) { jQuery(function($) {
$('#main_table').DataTable({ $('#main_table').DataTable({

View File

@ -17,7 +17,7 @@
</script> </script>
</head> </head>
<body> <body>
<% include navbar %> <%- include('navbar.ejs') -%>
<div class="container"> <div class="container">
<h2><%= db %></h2> <h2><%= db %></h2>
<table class="table"> <table class="table">
@ -60,6 +60,6 @@
<a class="btn btn-primary" onclick="restore_backup('<%= db %>')"><span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span> Restore</a> <a class="btn btn-primary" onclick="restore_backup('<%= db %>')"><span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span> Restore</a>
<a class="btn btn-danger" onclick="delete_backup('<%= db %>')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete</a> <a class="btn btn-danger" onclick="delete_backup('<%= db %>')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete</a>
</div> </div>
<% include popup.ejs %> <%- include('popup.ejs') -%>
</body> </body>
</html> </html>