Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.idea/
.build/
*.iml
config.js
node_modules/
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Pull base image.
FROM mhart/alpine-node-auto:6.2.0

ADD ./ /app

# Define working directory.
WORKDIR /app

# Installing app
RUN npm install --production

# Define default command.
CMD ["/bin/sh", "endpoint.sh"]

# Expose ports.
EXPOSE 3000
4 changes: 2 additions & 2 deletions config/development.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"port": 1337,
"redis": {
"host": "localhost",
"host": "redis",
"port": 6379
},
"errorPages": {
"404": "errors/404",
"not-connected": "errors/not-connected"
},
"development": true
"production": false
}
2 changes: 1 addition & 1 deletion config/production.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"port": 1337,
"redis": {
"host": "localhost",
"host": "redis",
"port": 6379
},
"errorPages": {
Expand Down
10 changes: 6 additions & 4 deletions controllers/active.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ module.exports = function (app) {
redisModel.getStatus("active").done(function(active){
redisModel.getJobsInList(active).done(function(keys){
redisModel.formatKeys(keys).done(function(formattedKeys){
redisModel.getProgressForKeys(formattedKeys).done(function(keyList){
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, active: true, type: "Active" };
dfd.resolve(model);
redisModel.getDataForKeys(formattedKeys).done(function(keyList) {
redisModel.getProgressForKeys(keyList).done(function (keyList) {
redisModel.getStatusCounts().done(function (countObject) {
var model = {keys: keyList, counts: countObject, active: true, type: "Active"};
dfd.resolve(model);
});
});
});
});
Expand Down
10 changes: 6 additions & 4 deletions controllers/complete.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ module.exports = function (app) {
var dfd = q.defer();
redisModel.getStatus("complete").done(function(completed){
redisModel.getJobsInList(completed).done(function(keys){
redisModel.formatKeys(keys).done(function(keyList){
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, complete: true, type: "Complete" };
dfd.resolve(model);
redisModel.formatKeys(keys).done(function(formattedKeys){
redisModel.getDataForKeys(formattedKeys).done(function(keyList) {
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, complete: true, type: "Complete" };
dfd.resolve(model);
});
});
});
});
Expand Down
8 changes: 5 additions & 3 deletions controllers/delayed.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ module.exports = function (app) {
redisModel.getJobsInList(delayed).done(function(keys){
redisModel.formatKeys(keys).done(function(formattedKeys){
redisModel.getDelayTimeForKeys(formattedKeys).done(function(keyList){
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, delayed: true, type: "Delayed" };
dfd.resolve(model);
redisModel.getDataForKeys(keyList).done(function(keyList) {
redisModel.getStatusCounts().done(function (countObject) {
var model = {keys: keyList, counts: countObject, delayed: true, type: "Delayed"};
dfd.resolve(model);
});
});
});
});
Expand Down
10 changes: 6 additions & 4 deletions controllers/failed.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ module.exports = function (app) {
var dfd = q.defer();
redisModel.getStatus("failed").done(function(failed){
redisModel.getJobsInList(failed).done(function(keys){
redisModel.formatKeys(keys).done(function(keyList){
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, failed: true, type: "Failed"};
dfd.resolve(model);
redisModel.formatKeys(keys).done(function(formattedKeys){
redisModel.getDataForKeys(formattedKeys).done(function(keyList) {
redisModel.getStatusCounts().done(function (countObject) {
var model = {keys: keyList, counts: countObject, failed: true, type: "Failed"};
dfd.resolve(model);
});
});
});
});
Expand Down
10 changes: 6 additions & 4 deletions controllers/pending.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ module.exports = function (app) {
var dfd = q.defer();
redisModel.getStatus("wait").done(function(active){
redisModel.getJobsInList(active).done(function(keys){
redisModel.formatKeys(keys).done(function(keyList){
redisModel.getStatusCounts().done(function(countObject){
var model = { keys: keyList, counts: countObject, pending: true, type: "Pending" };
dfd.resolve(model);
redisModel.formatKeys(keys).done(function(formattedKeys){
redisModel.getDataForKeys(formattedKeys).done(function(keyList) {
redisModel.getStatusCounts().done(function (countObject) {
var model = {keys: keyList, counts: countObject, pending: true, type: "Pending"};
dfd.resolve(model);
});
});
});
});
Expand Down
1 change: 1 addition & 0 deletions endpoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node index.js
4 changes: 4 additions & 0 deletions lib/setupAndMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var redisAdapter = require('./redisConnector'),
express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan');

module.exports = function(app, options){
options = options || {};
Expand All @@ -13,6 +14,9 @@ module.exports = function(app, options){
if (!options.redis){
throw new Error('No redis configuration options passed to matador');
}

app.use(morgan('dev'));

//Connect to redis
redisAdapter.connect(options.redis);
var redisConnectionEnforcer = require('./enforceConnection')(options);
Expand Down
42 changes: 37 additions & 5 deletions models/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,12 @@ var formatKeys = function(keys){
explodedKeys[1] = queue;
explodedKeys[2] = arr[arr.length-1];
var status = "stuck";
if(activeJobs.keys[explodedKeys[1]] && activeJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "active";
else if(completedJobs.keys[explodedKeys[1]] && completedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "complete";
else if(failedJobs.keys[explodedKeys[1]] && failedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "failed";
else if(pendingJobs.keys[explodedKeys[1]] && pendingJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "pending";
else if(delayedJobs.keys[explodedKeys[1]] && delayedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "delayed";
console.log(activeJobs, explodedKeys[1], typeof activeJobs.keys[explodedKeys[1]]);
if(activeJobs.keys[explodedKeys[1]] && typeof activeJobs.keys[explodedKeys[1]].indexOf === "function" && activeJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "active";
else if(completedJobs.keys[explodedKeys[1]] && typeof completedJobs.keys[explodedKeys[1]].indexOf === "function" && completedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "complete";
else if(failedJobs.keys[explodedKeys[1]] && typeof failedJobs.keys[explodedKeys[1]].indexOf === "function" && failedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "failed";
else if(pendingJobs.keys[explodedKeys[1]] && typeof pendingJobs.keys[explodedKeys[1]].indexOf === "function" && pendingJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "pending";
else if(delayedJobs.keys[explodedKeys[1]] && typeof delayedJobs.keys[explodedKeys[1]].indexOf === "function" && delayedJobs.keys[explodedKeys[1]].indexOf(explodedKeys[2]) !== -1) status = "delayed";
keyList.push({id: explodedKeys[2], type: explodedKeys[1], status: status});
}

Expand Down Expand Up @@ -413,6 +414,36 @@ var getProgressForKeys = function(keys){
return dfd.promise;
};

// var dfd = q.defer();
// if(!id) dfd.resolve({success: false, message: "There was no ID provided."});
// if(!type) dfd.resolve({success: false, message: "There was no type provided."});
//
// var firstPartOfKey = "bull:"+type+":";
// var multi = [];
// redis.hgetall(firstPartOfKey+id, function(err, data){
// if(err){
// dfd.resolve({success: false, message: err});
// }else{
// dfd.resolve({success: true, message: data});
// }
// });
// return dfd.promise;

var getDataForKeys = function(keys){
var dfd = q.defer();
var multi = [];
for(var i = 0, ii = keys.length; i < ii; i++){
multi.push(["hgetall", "bull:"+keys[i].type+":"+keys[i].id]);
}
redis.multi(multi).exec(function(err, results){
for(var i = 0, ii = keys.length; i < ii; i++){
keys[i].data = results[i] ? results[i].data : null;
}
dfd.resolve(keys);
});
return dfd.promise;
};

var getDelayTimeForKeys = function(keys){
var dfd = q.defer();
var multi = [];
Expand Down Expand Up @@ -482,5 +513,6 @@ module.exports.makePendingById = makePendingById; //Makes a job with a specific
module.exports.deleteJobByStatus = deleteJobByStatus; //Deletes all jobs in a specific status
module.exports.deleteJobById = deleteJobById; //Deletes a job by ID. Requires type as the first parameter and ID as the second.
module.exports.getProgressForKeys = getProgressForKeys; //Gets the progress for the keys passed in
module.exports.getDataForKeys = getDataForKeys; //Gets the progress for the keys passed in
module.exports.getDelayTimeForKeys = getDelayTimeForKeys; // Gets the delay end time for the keys passed in
module.exports.getQueues = getQueues //Get information about all the queues in the redis instance
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"express": "^4.13.4",
"less": "~2.1.2",
"lodash": "~2.4.1",
"morgan": "^1.7.0",
"q": "~1.1.2",
"redis": "^2.5.3"
},
Expand Down
28 changes: 26 additions & 2 deletions public/js/DataModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ var DataModel = function(){
_self.keys = ko.observableArray([]);
_self.memory = ko.observable({});
_self.peakMemory = ko.observable("");
_self.refreshTimeout = ko.observable(0);
_self.fetchingUpdates = ko.observable(false);

_self.autoRefreshId = null;
_self.fn = {
refreshViewModel: function(force){
var pathname = window.location.pathname.replace(window.basepath, '');
var refreshUrl = window.basepath + '/api' + (pathname !== '/' ? pathname : '');

var paused = true;
var refresh = function(){
_self.fetchingUpdates(true);
$.getJSON(refreshUrl).done(function(data){
_self.complete(" ("+data.counts.complete+")");
_self.failed(" ("+data.counts.failed+")");
Expand All @@ -32,13 +36,33 @@ var DataModel = function(){
_self.memory(data.memory.usage);
_self.peakMemory(data.memory.peak.human);
}
_self.fetchingUpdates(false);
paused = false;
});
};

var counter = 0;
var refreshTimer = function(pollInterval) {
if (paused) return;

if (counter % pollInterval === 0) {
paused = true;
_self.refreshTimeout(1);
refresh();
} else {
_self.refreshTimeout(((counter % pollInterval) / pollInterval));
}

counter++;
};

if(force){
clearInterval(_self.autoRefreshId);
refresh();
paused = false;
refreshTimer(0);
}
_self.autoRefreshId = setInterval(refresh, 2500);

_self.autoRefreshId = setInterval(refreshTimer, 100, 20);
}
}

Expand Down
17 changes: 13 additions & 4 deletions public/js/RedisHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var RedisHandler = function(){
var type = o.type;
_self.util.notyConfirm("Are you sure you want to delete the job of type "+ type + " with ID #"+id+"?", function(){
_self.util.blockUI();
$.getJSON(window.basepath + "/api/jobs/delete/id/"+type+"/"+id).done(function(response){
$.getJSON(window.basepath + "/api/jobs/delete/id/"+ encodeURIComponent(type) +"/"+id).done(function(response){
_self.util.handleAjaxResponse(response);
dataModel.fn.refreshViewModel(true);
}).always(function(){
Expand All @@ -32,6 +32,7 @@ var RedisHandler = function(){
});
},
deleteByStatus: function(status, obj){
obj = obj || {};
var queueName = obj.name;
status = status.toLowerCase();
var statusDisplay = status;
Expand Down Expand Up @@ -63,7 +64,7 @@ var RedisHandler = function(){
var type = o.type;
_self.util.notyConfirm("Are you sure you want make the job of type "+ type + " with ID #"+id+" pending? This will put this job in the queue to be run again.", function(){
_self.util.blockUI();
$.getJSON(window.basepath + "/api/jobs/pending/id/"+type+"/"+id).done(function(response){
$.getJSON(window.basepath + "/api/jobs/pending/id/"+encodeURIComponent(type)+"/"+id).done(function(response){
_self.util.handleAjaxResponse(response);
dataModel.fn.refreshViewModel(true);
}).always(function(){
Expand All @@ -75,7 +76,7 @@ var RedisHandler = function(){
var id = o.id;
var type = o.type;
_self.util.blockUI();
$.getJSON(window.basepath + "/api/jobs/info/"+type+"/"+id).done(function(response){
$.getJSON(window.basepath + "/api/jobs/info/"+encodeURIComponent(type)+"/"+id).done(function(response){
if(response.success === false){
_self.util.handleAjaxResponse(response);
}else{
Expand All @@ -87,11 +88,19 @@ var RedisHandler = function(){
response.message.data = JSON.parse(response.message.data);
var data = JSON.stringify(response.message.data, null, 2);
var stacktrace = response.message.stacktrace;
var returnvalue = response.message.returnvalue;

try {
returnvalue = JSON.stringify(JSON.parse(returnvalue), null, 2);
} catch (e) {
console.warn('Cannot prettify returnvalue', e);
}

var message = '<pre style="text-align: left">Job ID: ' + id +
'\nType: ' + type +
'\nStatus: ' + o.status +
'\n\nData: ' + data;
'\n\nData: ' + data +
'\n\nReturn value: ' + returnvalue;

if (stacktrace){
message = message + '\n\n<span style="color: red;">Stack Trace: \n' + stacktrace + '</span>' + '</pre>';
Expand Down
5 changes: 4 additions & 1 deletion public/templates/jobList.dust
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ Matador
<tbody data-bind="foreach: keys">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: type"></td>
<td>
<div data-bind="text: type"></div>
<div data-bind="visible: typeof data != 'undefined', text: typeof data != 'undefined' ? data : ''" style="font-size: 80%; color: rgba(0, 0, 0, 0.5)"></div>
</td>
<td data-bind="text: status"></td>
<td data-bind="visible: keys.length > 0 && keys[0].progress != undefined, text: keys[0].progress != undefined ? progress : ''"></td>
<td data-bind="visible: keys.length > 0 && keys[0].delayUntil != undefined, text: keys[0].delayUntil != undefined ? delayUntil : ''"></td>
Expand Down
7 changes: 6 additions & 1 deletion public/templates/layouts/master.dust
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<![endif]-->

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="{basepath}/js/knockout.min.js"></script>
<!--<script src="{basepath}/js/knockout.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<script src="{basepath}/js/jquery.blockUI.js"></script>
<script src="{basepath}/js/jquery.noty.packaged.min.js"></script>
<script src="{basepath}/js/bootstrap.min.js"></script>
Expand Down Expand Up @@ -73,6 +74,10 @@
<li {?delayed}class="active"{/delayed}><a href="{basepath}/delayed">Delayed<span data-bind="visible: delayed() != null, text: delayed"></span></a></li>
<li {?queues}class="active"{/queues}><a href="{basepath}/queues">Queues<span data-bind="visible: queues() != null, text: queues"></span></a></li>
<li {?newjob}class="active"{/newjob}><a href="{basepath}/newjob">New Job</a></li>
<li style="text-align: center">
<progress data-bind="visible: !fetchingUpdates(), attr: { value: refreshTimeout() }" style="width: 50%; height: 10px;"></progress>
<div data-bind="visible: fetchingUpdates()">Fetching updates...</div>
</li>
</ul>
</div>

Expand Down
2 changes: 1 addition & 1 deletion public/templates/newJob.dust
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Matador
<br>
<label for="payload">Payload:</label>
<br>
<textarea name="payload" id="textarea" class="form-control" cols="45" rows="5" ></textarea>
<textarea name="payload" id="textarea" style="font-family: monospace;" class="form-control" cols="45" rows="5" ></textarea>
<br>
<input onclick="redisHandler.fn.createJob()" id="button" class="btn btn-default" value="Create" />
</div>
Expand Down