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 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