que-web 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f961c975dc887e5c769088ab961dc143d91b810a
4
- data.tar.gz: cf90a843935e7ff3918c8ea6f5f21e2cb31097f6
3
+ metadata.gz: 472d12b5889358f528fe9de93f81177f72512635
4
+ data.tar.gz: 16c433810179716c6f9be25fb32a15b0496c3c48
5
5
  SHA512:
6
- metadata.gz: ec34ca05e2e6ea188dfd667fc666a484e6b3f757fc660350c9e4596f603433dd9a60963ea58886ead379af07e081988a175f0c77a914b38d3b5797d21d9d5356
7
- data.tar.gz: 61dc356e013622a56334b7813e82ea62033e5f6c57f3e7a3ab5f4ccf6472bc3d9ceae1b6dc611ec9783ee742b372bbac19e4182ca2171a04fc172f1b0e8eadac
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
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gem 'que-web'
3
+ gem 'sequel'
4
+ gem 'pg'
5
+ gem 'puma'
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'sequel'
2
+ require "que"
3
+ require "que/web"
4
+
5
+ DB = Sequel.connect(ENV['DATABASE_URL'])
6
+ Que.connection = DB
7
+
8
+ map "/" do
9
+ run Que::Web
10
+ end
@@ -2,12 +2,14 @@ PATH
2
2
  remote: ../../
3
3
  specs:
4
4
  que-web (0.2.2)
5
+ erubis
5
6
  que (~> 0.8)
6
7
  sinatra
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
12
+ erubis (2.7.0)
11
13
  pg (0.17.1)
12
14
  que (0.8.2)
13
15
  rack (1.5.2)
@@ -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
- Que.execute SQL[:reschedule_job], [job_id, Time.now]
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.2.2"
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")});
@@ -56,3 +56,7 @@ button.plain {
56
56
  .form-inline {
57
57
  display: inline-block;
58
58
  }
59
+ pre {
60
+ white-space: pre-wrap;
61
+ word-wrap: break-word;
62
+ }
@@ -0,0 +1,3 @@
1
+ <% flash.each do |key,val| %>
2
+ <div data-alert class="alert-box <%= key %>"><%= val %></div>
3
+ <% end %>
@@ -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}" %>"><%= job.run_at.utc %></a></td>
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.args.map(&:inspect).join(', ') %></pre></td>
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
- <%= erb :_navbar %>
11
- <%= yield %>
12
- <%= erb :_footer %>
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>
@@ -17,7 +17,7 @@
17
17
  <tbody>
18
18
  <% @list.page_jobs.each do |job| %>
19
19
  <tr>
20
- <td><%= job.pg_state_changed_at.utc %></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>
@@ -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}" %>"><%= job.run_at.utc %></a></td>
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.args.map(&:inspect).join(', ') %></pre></td>
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
@@ -21,7 +21,7 @@
21
21
  </tr>
22
22
  <tr>
23
23
  <th>Run At</th>
24
- <td><%= @job.run_at.utc %></td>
24
+ <td><%== relative_time @job.run_at %></td>
25
25
  </tr>
26
26
  <tr>
27
27
  <th>Failures</th>
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.2.2
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-14 00:00:00.000000000 Z
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