que-web 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +6 -0
- data/docker/Dockerfile +22 -0
- data/docker/Gemfile +5 -0
- data/docker/Gemfile.lock +28 -0
- data/docker/config.ru +10 -0
- data/examples/rack/Gemfile.lock +2 -0
- data/examples/rack/config.ru +9 -1
- data/lib/que/web.rb +42 -1
- data/que-web.gemspec +2 -1
- data/web/public/js/{vendor/jquery.js → jquery.min.js} +0 -0
- data/web/public/js/jquery.timeago.min.js +17 -0
- data/web/public/styles/application.css +4 -0
- data/web/views/_flash.erb +3 -0
- data/web/views/failing.erb +4 -2
- data/web/views/layout.erb +12 -3
- data/web/views/running.erb +1 -1
- data/web/views/scheduled.erb +2 -2
- data/web/views/show.erb +1 -1
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 472d12b5889358f528fe9de93f81177f72512635
|
4
|
+
data.tar.gz: 16c433810179716c6f9be25fb32a15b0496c3c48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05b564a90c42a676e7b4fc297d1fa2965bf930315809df2faa9b88e733a94e505a2aa3f1eb75fce07c0f7bf3ffe41895f704bb89f00549ff5da093c14d64c2c3
|
7
|
+
data.tar.gz: 6048a2d8a906e3fd0f881e917ea2cdf2532ffca7e8c0df1a1c79d66275b5032d21de1467aa5b385667b34ac550dc4d6fecf962f7d2394c9f40a2a6c259dd3251
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
### 0.3.0 - 2014-11-24
|
2
|
+
#### Added:
|
3
|
+
- Dockerfile #1
|
4
|
+
- Show errors in list view #7
|
5
|
+
- Relative "x minutes from now" dates for all visible dates #3
|
6
|
+
- Display flash messages on delete or run immediate actions (only if session available) #6
|
7
|
+
- CHANGELOG file
|
8
|
+
|
9
|
+
#### Fixed:
|
10
|
+
- Large argument lists now wrap in list view #8
|
11
|
+
|
12
|
+
#### Security:
|
13
|
+
- Use Erubis to escape html by default #9
|
14
|
+
|
15
|
+
|
16
|
+
### 0.2.2 - 2014-11-14
|
17
|
+
#### Added:
|
18
|
+
- Working dashboard
|
19
|
+
- List running, scheduled, and failing jobs
|
20
|
+
- Delete jobs
|
21
|
+
- Run jobs immediately
|
22
|
+
- Show job details
|
data/README.md
CHANGED
@@ -38,3 +38,9 @@ Or in Rails `config/routes.rb`
|
|
38
38
|
require "que/web"
|
39
39
|
mount Que::Web => "/que"
|
40
40
|
```
|
41
|
+
|
42
|
+
If you want to use Docker, run:
|
43
|
+
```
|
44
|
+
docker run -e DATABASE_URL=postgres://username:password@hostname/db_name -p 3002:8080 joevandyk/que-web
|
45
|
+
```
|
46
|
+
Or use docker/Dockerfile to build your own container.
|
data/docker/Dockerfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# There's an image at joevandyk/que-web.
|
2
|
+
# Run like:
|
3
|
+
# docker run -e DATABASE_URL=postgres://username:password@hostname/db_name -p 3002:8080 joevandyk/que-web
|
4
|
+
|
5
|
+
FROM dockerfile/ruby
|
6
|
+
|
7
|
+
# Define working directory.
|
8
|
+
WORKDIR /app
|
9
|
+
|
10
|
+
EXPOSE 8080
|
11
|
+
|
12
|
+
# Define default command.
|
13
|
+
CMD bundle exec puma -e production -p 8080 /app/config.ru
|
14
|
+
|
15
|
+
RUN apt-get update && \
|
16
|
+
apt-get install libpq-dev -y && \
|
17
|
+
rm -rf /var/cache/apt/* /var/lib/apt/lists/*
|
18
|
+
|
19
|
+
|
20
|
+
ADD . /app
|
21
|
+
RUN bundle install
|
22
|
+
|
data/docker/Gemfile
ADDED
data/docker/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
pg (0.17.1)
|
5
|
+
puma (2.9.2)
|
6
|
+
rack (>= 1.1, < 2.0)
|
7
|
+
que (0.8.2)
|
8
|
+
que-web (0.2.2)
|
9
|
+
que (~> 0.8)
|
10
|
+
sinatra
|
11
|
+
rack (1.5.2)
|
12
|
+
rack-protection (1.5.3)
|
13
|
+
rack
|
14
|
+
sequel (4.16.0)
|
15
|
+
sinatra (1.4.5)
|
16
|
+
rack (~> 1.4)
|
17
|
+
rack-protection (~> 1.4)
|
18
|
+
tilt (~> 1.3, >= 1.3.4)
|
19
|
+
tilt (1.4.1)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
pg
|
26
|
+
puma
|
27
|
+
que-web
|
28
|
+
sequel
|
data/docker/config.ru
ADDED
data/examples/rack/Gemfile.lock
CHANGED
data/examples/rack/config.ru
CHANGED
@@ -2,6 +2,7 @@ require File.expand_path('../boot', __FILE__)
|
|
2
2
|
require 'que/web'
|
3
3
|
|
4
4
|
map '/que' do
|
5
|
+
use Rack::Session::Cookie, :secret => 'insecure', :key => 'que.examples.rack'
|
5
6
|
run Que::Web
|
6
7
|
end
|
7
8
|
|
@@ -14,7 +15,14 @@ end
|
|
14
15
|
|
15
16
|
map '/fail' do
|
16
17
|
run lambda { |env|
|
17
|
-
FailJob.enqueue 'arg1', {name: 'fail', age: 20}
|
18
|
+
FailJob.enqueue 'arg1', {name: 'fail', age: 20, numbers: [10]*50}
|
19
|
+
[200, {}, ['Failing job queued']]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
map '/xss' do
|
24
|
+
run lambda { |env|
|
25
|
+
FailJob.enqueue '<script>alert("xss")</script>', {name: '<script>alert("xss")', age: 20, numbers: [10]*50}
|
18
26
|
[200, {}, ['Failing job queued']]
|
19
27
|
}
|
20
28
|
end
|
data/lib/que/web.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
require "sinatra"
|
2
|
+
require "erubis"
|
2
3
|
|
3
4
|
module Que
|
4
5
|
class Web < Sinatra::Base
|
5
6
|
PAGE_SIZE = 10
|
7
|
+
FLASH_KEY = 'que.web.flash'.freeze
|
6
8
|
|
7
9
|
use Rack::MethodOverride
|
8
10
|
|
9
11
|
set :root, File.expand_path("../../../web", __FILE__)
|
10
12
|
set :public_folder, proc { "#{root}/public" }
|
11
13
|
set :views, proc { File.expand_path("views", root) }
|
14
|
+
set :erb, :escape_html => true
|
12
15
|
|
13
16
|
get "/" do
|
14
17
|
stats = Que.execute SQL[:dashboard_stats]
|
@@ -58,9 +61,12 @@ module Que
|
|
58
61
|
put "/jobs/:id" do |id|
|
59
62
|
job_id = id.to_i
|
60
63
|
if job_id > 0
|
61
|
-
|
64
|
+
run_at = Time.now
|
65
|
+
Que.execute SQL[:reschedule_job], [job_id, run_at]
|
66
|
+
set_flash "info", "Job #{job_id} rescheduled for #{run_at}"
|
62
67
|
end
|
63
68
|
|
69
|
+
|
64
70
|
redirect request.referrer, 303
|
65
71
|
end
|
66
72
|
|
@@ -68,6 +74,7 @@ module Que
|
|
68
74
|
job_id = id.to_i
|
69
75
|
if job_id > 0
|
70
76
|
Que.execute SQL[:delete_job], [job_id]
|
77
|
+
set_flash "warning", "Job #{job_id} deleted"
|
71
78
|
end
|
72
79
|
|
73
80
|
redirect request.referrer, 303
|
@@ -78,6 +85,8 @@ module Que
|
|
78
85
|
Pager.new(page, PAGE_SIZE, record_count)
|
79
86
|
end
|
80
87
|
|
88
|
+
after { session['flash'] = {} if @sweep_flash }
|
89
|
+
|
81
90
|
helpers do
|
82
91
|
def root_path
|
83
92
|
"#{env['SCRIPT_NAME']}/"
|
@@ -88,6 +97,38 @@ module Que
|
|
88
97
|
"active"
|
89
98
|
end
|
90
99
|
end
|
100
|
+
|
101
|
+
def format_args(job)
|
102
|
+
truncate job.args.map(&:inspect).join(', ')
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_error(job)
|
106
|
+
return unless job.last_error
|
107
|
+
line = job.last_error.lines.first
|
108
|
+
truncate line, 30
|
109
|
+
end
|
110
|
+
|
111
|
+
def relative_time(time)
|
112
|
+
%{<time class="timeago" datetime="#{time.utc.iso8601}">#{time.utc}</time>}
|
113
|
+
end
|
114
|
+
|
115
|
+
def truncate(str, len=200)
|
116
|
+
if str.length > len
|
117
|
+
str[0..len] + '...'
|
118
|
+
else
|
119
|
+
str
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def flash
|
124
|
+
@sweep_flash = true
|
125
|
+
session[FLASH_KEY] ||= {}
|
126
|
+
end
|
127
|
+
|
128
|
+
def set_flash(level, val)
|
129
|
+
hash = session[FLASH_KEY] ||= {}
|
130
|
+
hash[level] = val
|
131
|
+
end
|
91
132
|
end
|
92
133
|
end
|
93
134
|
end
|
data/que-web.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "que-web"
|
7
|
-
spec.version = "0.
|
7
|
+
spec.version = "0.3.0"
|
8
8
|
spec.authors = ["Jason Staten"]
|
9
9
|
spec.email = ["jstaten07@gmail.com"]
|
10
10
|
spec.summary = %q{A web interface for the que queue}
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_dependency "que", "~> 0.8"
|
21
21
|
spec.add_dependency "sinatra"
|
22
|
+
spec.add_dependency "erubis"
|
22
23
|
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
24
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/**
|
2
|
+
* Timeago is a jQuery plugin that makes it easy to support automatically
|
3
|
+
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
|
4
|
+
*
|
5
|
+
* @name timeago
|
6
|
+
* @version 1.4.1
|
7
|
+
* @requires jQuery v1.2.3+
|
8
|
+
* @author Ryan McGeary
|
9
|
+
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
|
10
|
+
*
|
11
|
+
* For usage and examples, visit:
|
12
|
+
* http://timeago.yarp.com/
|
13
|
+
*
|
14
|
+
* Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
|
15
|
+
*/
|
16
|
+
|
17
|
+
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function d(){var c=e(this),d=b.settings;return isNaN(c.datetime)||(0==d.cutoff||Math.abs(g(c.datetime))<d.cutoff)&&a(this).text(f(c.datetime)),this}function e(c){if(c=a(c),!c.data("timeago")){c.data("timeago",{datetime:b.datetime(c)});var d=a.trim(c.text());b.settings.localeTitle?c.attr("title",c.data("timeago").datetime.toLocaleString()):!(d.length>0)||b.isTime(c)&&c.attr("title")||c.attr("title",d)}return c.data("timeago")}function f(a){return b.inWords(g(a))}function g(a){return(new Date).getTime()-a.getTime()}a.timeago=function(b){return b instanceof Date?f(b):"string"==typeof b?f(a.timeago.parse(b)):"number"==typeof b?f(new Date(b)):f(a.timeago.datetime(b))};var b=a.timeago;a.extend(a.timeago,{settings:{refreshMillis:6e4,allowPast:!0,allowFuture:!1,localeTitle:!1,cutoff:0,strings:{prefixAgo:null,prefixFromNow:null,suffixAgo:"ago",suffixFromNow:"from now",inPast:"any moment now",seconds:"less than a minute",minute:"about a minute",minutes:"%d minutes",hour:"about an hour",hours:"about %d hours",day:"a day",days:"%d days",month:"about a month",months:"%d months",year:"about a year",years:"%d years",wordSeparator:" ",numbers:[]}},inWords:function(b){function k(d,e){var f=a.isFunction(d)?d(e,b):d,g=c.numbers&&c.numbers[e]||e;return f.replace(/%d/i,g)}if(!this.settings.allowPast&&!this.settings.allowFuture)throw"timeago allowPast and allowFuture settings can not both be set to false.";var c=this.settings.strings,d=c.prefixAgo,e=c.suffixAgo;if(this.settings.allowFuture&&0>b&&(d=c.prefixFromNow,e=c.suffixFromNow),!this.settings.allowPast&&b>=0)return this.settings.strings.inPast;var f=Math.abs(b)/1e3,g=f/60,h=g/60,i=h/24,j=i/365,l=45>f&&k(c.seconds,Math.round(f))||90>f&&k(c.minute,1)||45>g&&k(c.minutes,Math.round(g))||90>g&&k(c.hour,1)||24>h&&k(c.hours,Math.round(h))||42>h&&k(c.day,1)||30>i&&k(c.days,Math.round(i))||45>i&&k(c.month,1)||365>i&&k(c.months,Math.round(i/30))||1.5>j&&k(c.year,1)||k(c.years,Math.round(j)),m=c.wordSeparator||"";return void 0===c.wordSeparator&&(m=" "),a.trim([d,l,e].join(m))},parse:function(b){var c=a.trim(b);return c=c.replace(/\.\d+/,""),c=c.replace(/-/,"/").replace(/-/,"/"),c=c.replace(/T/," ").replace(/Z/," UTC"),c=c.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"),c=c.replace(/([\+\-]\d\d)$/," $100"),new Date(c)},datetime:function(c){var d=b.isTime(c)?a(c).attr("datetime"):a(c).attr("title");return b.parse(d)},isTime:function(b){return"time"===a(b).get(0).tagName.toLowerCase()}});var c={init:function(){var c=a.proxy(d,this);c();var e=b.settings;e.refreshMillis>0&&(this._timeagoInterval=setInterval(c,e.refreshMillis))},update:function(c){var e=b.parse(c);a(this).data("timeago",{datetime:e}),b.settings.localeTitle&&a(this).attr("title",e.toLocaleString()),d.apply(this)},updateFromDOM:function(){a(this).data("timeago",{datetime:b.parse(b.isTime(this)?a(this).attr("datetime"):a(this).attr("title"))}),d.apply(this)},dispose:function(){this._timeagoInterval&&(window.clearInterval(this._timeagoInterval),this._timeagoInterval=null)}};a.fn.timeago=function(a,b){var d=a?c[a]:c.init;if(!d)throw new Error("Unknown function name '"+a+"' for timeago");return this.each(function(){d.call(this,b)}),this},document.createElement("abbr"),document.createElement("time")});
|
data/web/views/failing.erb
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
<th>Job</th>
|
14
14
|
<th>Queue</th>
|
15
15
|
<th>Args</th>
|
16
|
+
<th>Error</th>
|
16
17
|
<th></th>
|
17
18
|
<th></th>
|
18
19
|
</tr>
|
@@ -20,11 +21,12 @@
|
|
20
21
|
<tbody>
|
21
22
|
<% @list.page_jobs.each do |job| %>
|
22
23
|
<tr>
|
23
|
-
<td><a href="<%= to "jobs/#{job.job_id}" %>"
|
24
|
+
<td><a href="<%= to "jobs/#{job.job_id}" %>"><%== relative_time job.run_at %></a></td>
|
24
25
|
<td><%= job.error_count %></td>
|
25
26
|
<td><%= job.job_class %></td>
|
26
27
|
<td><%= job.queue %></td>
|
27
|
-
<td><pre><%= job
|
28
|
+
<td><pre><%= format_args job %></pre></td>
|
29
|
+
<td><%= format_error job %></pre></td>
|
28
30
|
<td>
|
29
31
|
<form action="<%= to "jobs/#{job.job_id}" %>" method="post">
|
30
32
|
<input type="hidden" name="_method" value="put" />
|
data/web/views/layout.erb
CHANGED
@@ -7,8 +7,17 @@
|
|
7
7
|
<link rel="stylesheet" href="<%= root_path %>styles/font-awesome.min.css" />
|
8
8
|
<link rel="stylesheet" href="<%= root_path %>styles/application.css" />
|
9
9
|
<body>
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
<%== erb :_navbar %>
|
11
|
+
<%== erb :_flash %>
|
12
|
+
<%== yield %>
|
13
|
+
<%== erb :_footer %>
|
14
|
+
<script src="<%= root_path %>js/jquery.min.js"></script>
|
15
|
+
<script src="<%= root_path %>js/jquery.timeago.min.js"></script>
|
16
|
+
<script>
|
17
|
+
(function($){
|
18
|
+
$.timeago.settings.allowFuture = true;
|
19
|
+
$(".timeago").timeago();
|
20
|
+
}(jQuery));
|
21
|
+
</script>
|
13
22
|
</body>
|
14
23
|
</html>
|
data/web/views/running.erb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
<tbody>
|
18
18
|
<% @list.page_jobs.each do |job| %>
|
19
19
|
<tr>
|
20
|
-
<td
|
20
|
+
<td><%== relative_time job.pg_state_changed_at %></td>
|
21
21
|
<td><%= job.job_class %></td>
|
22
22
|
<td><%= job.queue %></td>
|
23
23
|
<td><pre><%= job.args.map(&:inspect).join(', ') %></pre></td>
|
data/web/views/scheduled.erb
CHANGED
@@ -19,10 +19,10 @@
|
|
19
19
|
<tbody>
|
20
20
|
<% @list.page_jobs.each do |job| %>
|
21
21
|
<tr>
|
22
|
-
<td><a href="<%= to "jobs/#{job.job_id}" %>"
|
22
|
+
<td><a href="<%= to "jobs/#{job.job_id}" %>"><%== relative_time job.run_at %></a></td>
|
23
23
|
<td><%= job.job_class %></td>
|
24
24
|
<td><%= job.queue %></td>
|
25
|
-
<td><pre><%= job
|
25
|
+
<td><pre><%= format_args job %></pre></td>
|
26
26
|
<td>
|
27
27
|
<form action="<%= to "jobs/#{job.job_id}" %>" method="post">
|
28
28
|
<input type="hidden" name="_method" value="put" />
|
data/web/views/show.erb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que-web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Staten
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: que
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: erubis
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,11 +103,16 @@ extra_rdoc_files: []
|
|
89
103
|
files:
|
90
104
|
- ".gitignore"
|
91
105
|
- ".travis.yml"
|
106
|
+
- CHANGELOG.md
|
92
107
|
- Gemfile
|
93
108
|
- LICENSE.txt
|
94
109
|
- README.md
|
95
110
|
- Rakefile
|
96
111
|
- doc/queweb.png
|
112
|
+
- docker/Dockerfile
|
113
|
+
- docker/Gemfile
|
114
|
+
- docker/Gemfile.lock
|
115
|
+
- docker/config.ru
|
97
116
|
- examples/rack/Gemfile
|
98
117
|
- examples/rack/Gemfile.lock
|
99
118
|
- examples/rack/boot.rb
|
@@ -117,15 +136,17 @@ files:
|
|
117
136
|
- web/public/fonts/fontawesome-webfont.ttf
|
118
137
|
- web/public/fonts/fontawesome-webfont.woff
|
119
138
|
- web/public/js/foundation.min.js
|
139
|
+
- web/public/js/jquery.min.js
|
140
|
+
- web/public/js/jquery.timeago.min.js
|
120
141
|
- web/public/js/vendor/fastclick.js
|
121
142
|
- web/public/js/vendor/jquery.cookie.js
|
122
|
-
- web/public/js/vendor/jquery.js
|
123
143
|
- web/public/js/vendor/modernizr.js
|
124
144
|
- web/public/js/vendor/placeholder.js
|
125
145
|
- web/public/styles/application.css
|
126
146
|
- web/public/styles/font-awesome.min.css
|
127
147
|
- web/public/styles/foundation.min.css
|
128
148
|
- web/public/styles/normalize.css
|
149
|
+
- web/views/_flash.erb
|
129
150
|
- web/views/_footer.erb
|
130
151
|
- web/views/_navbar.erb
|
131
152
|
- web/views/_pager.erb
|