solid_queue_dashboard 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +5 -1
- data/Procfile.dev +2 -0
- data/README.md +73 -21
- data/app/assets/javascripts/solid_queue_dashboard/alpine.js +5 -0
- data/app/assets/javascripts/solid_queue_dashboard/application.js +46 -0
- data/app/assets/stylesheets/solid_queue_dashboard/application.css +2049 -0
- data/app/assets/stylesheets/solid_queue_dashboard/tailwind.css +554 -0
- data/app/controllers/solid_queue_dashboard/appearance_controller.rb +8 -0
- data/app/controllers/solid_queue_dashboard/application_controller.rb +7 -0
- data/app/controllers/solid_queue_dashboard/dashboard_controller.rb +8 -0
- data/app/controllers/solid_queue_dashboard/jobs_controller.rb +42 -0
- data/app/controllers/solid_queue_dashboard/processes_controller.rb +28 -0
- data/app/controllers/solid_queue_dashboard/recurring_tasks_controller.rb +30 -0
- data/app/helpers/solid_queue_dashboard/appearance_helper.rb +7 -0
- data/app/helpers/solid_queue_dashboard/application_helper.rb +7 -0
- data/app/helpers/solid_queue_dashboard/icons_helper.rb +231 -0
- data/app/helpers/solid_queue_dashboard/jobs_helper.rb +51 -0
- data/app/helpers/solid_queue_dashboard/pagination_helper.rb +38 -0
- data/app/helpers/solid_queue_dashboard/processes_helper.rb +35 -0
- data/app/helpers/solid_queue_dashboard/recurring_tasks_helper.rb +33 -0
- data/app/views/layouts/solid_queue_dashboard/application.html.erb +21 -0
- data/app/views/solid_queue_dashboard/application/_flash_messages.html.erb +38 -0
- data/app/views/solid_queue_dashboard/application/_footer.html.erb +11 -0
- data/app/views/solid_queue_dashboard/application/_navbar.html.erb +50 -0
- data/app/views/solid_queue_dashboard/application/_pagination.html.erb +19 -0
- data/app/views/solid_queue_dashboard/dashboard/index.html.erb +41 -0
- data/app/views/solid_queue_dashboard/jobs/_filters.html.erb +87 -0
- data/app/views/solid_queue_dashboard/jobs/_table.html.erb +19 -0
- data/app/views/solid_queue_dashboard/jobs/_table_row.html.erb +82 -0
- data/app/views/solid_queue_dashboard/jobs/index.html.erb +57 -0
- data/app/views/solid_queue_dashboard/jobs/show.html.erb +192 -0
- data/app/views/solid_queue_dashboard/processes/_filters.html.erb +64 -0
- data/app/views/solid_queue_dashboard/processes/_table.html.erb +18 -0
- data/app/views/solid_queue_dashboard/processes/_table_row.html.erb +32 -0
- data/app/views/solid_queue_dashboard/processes/index.html.erb +27 -0
- data/app/views/solid_queue_dashboard/processes/show.html.erb +79 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_filters.html.erb +26 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_table.html.erb +19 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_table_row.html.erb +31 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/index.html.erb +21 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/show.html.erb +129 -0
- data/bun.lockb +0 -0
- data/config/routes.rb +18 -0
- data/lib/solid_queue_dashboard/configuration.rb +17 -0
- data/lib/solid_queue_dashboard/decorators/job_decorator.rb +63 -0
- data/lib/solid_queue_dashboard/decorators/jobs_decorator.rb +74 -0
- data/lib/solid_queue_dashboard/decorators/process_decorator.rb +13 -0
- data/lib/solid_queue_dashboard/decorators/processes_decorator.rb +15 -0
- data/lib/solid_queue_dashboard/decorators/recurring_task_decorator.rb +22 -0
- data/lib/solid_queue_dashboard/decorators/recurring_tasks_decorator.rb +26 -0
- data/lib/solid_queue_dashboard/engine.rb +13 -0
- data/lib/solid_queue_dashboard/job.rb +26 -0
- data/lib/solid_queue_dashboard/process.rb +24 -0
- data/lib/solid_queue_dashboard/recurring_task.rb +14 -0
- data/lib/solid_queue_dashboard/version.rb +1 -1
- data/lib/solid_queue_dashboard.rb +39 -1
- data/package.json +13 -0
- data/tailwind.config.js +83 -0
- metadata +73 -4
@@ -0,0 +1,21 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<div class="card-header border-b">
|
3
|
+
<h2 class="card-title">Recurring Tasks</h2>
|
4
|
+
|
5
|
+
<div class="!mt-4">
|
6
|
+
<%= render "solid_queue_dashboard/recurring_tasks/filters" %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="card-content !p-0">
|
11
|
+
<% if @recurring_tasks.any? %>
|
12
|
+
<%= render "solid_queue_dashboard/recurring_tasks/table", recurring_tasks: @recurring_tasks %>
|
13
|
+
<% else %>
|
14
|
+
<div class="px-6 py-12">
|
15
|
+
<p class="text-muted-foreground text-center">
|
16
|
+
No recurring tasks found
|
17
|
+
</p>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
</div>
|
@@ -0,0 +1,129 @@
|
|
1
|
+
<h1 class="text-4xl flex items-center gap-2">
|
2
|
+
<%= recurring_task_circle(@recurring_task.type, class: "size-4") %>
|
3
|
+
|
4
|
+
<span class="tracking-tighter font-bold">
|
5
|
+
Recurring Task #<%= @recurring_task.id %>
|
6
|
+
</span>
|
7
|
+
</h1>
|
8
|
+
|
9
|
+
<div class="card mt-6">
|
10
|
+
<div class="card-content pt-6 grid grid-cols-1 sm:grid-cols-2 sm:gap-16">
|
11
|
+
<div class="space-y-4">
|
12
|
+
<div class="info-line">
|
13
|
+
<span class="info-line-label">Type</span>
|
14
|
+
<div class="info-line-separator"></div>
|
15
|
+
<span class="info-line-value"><%= recurring_task_type_badge(@recurring_task.type) %></span>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="info-line">
|
19
|
+
<span class="info-line-label">Key</span>
|
20
|
+
<div class="info-line-separator"></div>
|
21
|
+
<span class="info-line-value"><%= @recurring_task.key %></span>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="info-line">
|
25
|
+
<span class="info-line-label">Schedule</span>
|
26
|
+
<div class="info-line-separator"></div>
|
27
|
+
<span class="info-line-value"><%= @recurring_task.schedule %></span>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% if @recurring_task.queue_name? %>
|
31
|
+
<div class="info-line">
|
32
|
+
<span class="info-line-label">Queue Name</span>
|
33
|
+
<div class="info-line-separator"></div>
|
34
|
+
<span class="info-line-value"><%= @recurring_task.queue_name %></span>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<div class="space-y-4">
|
40
|
+
<% if @recurring_task.priority? %>
|
41
|
+
<div class="info-line">
|
42
|
+
<span class="info-line-label">Priority</span>
|
43
|
+
<div class="info-line-separator"></div>
|
44
|
+
<span class="info-line-value"><%= @recurring_task.priority %></span>
|
45
|
+
</div>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<div class="info-line">
|
49
|
+
<span class="info-line-label">Static</span>
|
50
|
+
<div class="info-line-separator"></div>
|
51
|
+
<span class="info-line-value"><%= @recurring_task.static ? "Yes" : "No" %></span>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
<div class="info-line">
|
55
|
+
<span class="info-line-label">Created At</span>
|
56
|
+
<div class="info-line-separator"></div>
|
57
|
+
<span class="info-line-value" data-date><%= @recurring_task.created_at.to_fs(:database) %></span>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="info-line">
|
61
|
+
<span class="info-line-label">Updated At</span>
|
62
|
+
<div class="info-line-separator"></div>
|
63
|
+
<span class="info-line-value" data-date><%= @recurring_task.updated_at.to_fs(:database) %></span>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
|
69
|
+
<% if @recurring_task.command.present? %>
|
70
|
+
<div class="card mt-8 overflow-hidden">
|
71
|
+
<div class="card-header border-b border-t-4 border-t-amber-500 dark:border-t-amber-600">
|
72
|
+
<h3 class="card-title">Command</h3>
|
73
|
+
<p class="card-description">The command to be executed</p>
|
74
|
+
</div>
|
75
|
+
<div class="card-content pt-6">
|
76
|
+
<pre><%= @recurring_task.command %></pre>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
<% end %>
|
80
|
+
|
81
|
+
<% if @recurring_task.class_name.present? %>
|
82
|
+
<div class="card mt-8 overflow-hidden">
|
83
|
+
<div class="card-header border-b border-t-4 border-t-blue-500 dark:border-t-blue-600">
|
84
|
+
<h3 class="card-title">Job Class</h3>
|
85
|
+
<p class="card-description">The job class to be executed</p>
|
86
|
+
</div>
|
87
|
+
<div class="card-content pt-6">
|
88
|
+
<pre><%= @recurring_task.class_name %></pre>
|
89
|
+
</div>
|
90
|
+
</div>
|
91
|
+
<% end %>
|
92
|
+
|
93
|
+
<% if @recurring_task.arguments.present? %>
|
94
|
+
<div class="card mt-8 overflow-hidden">
|
95
|
+
<div class="card-header border-b border-t-4 border-t-purple-500 dark:border-t-purple-600">
|
96
|
+
<h3 class="card-title">Arguments</h3>
|
97
|
+
<p class="card-description">The arguments to be passed to the job</p>
|
98
|
+
</div>
|
99
|
+
<div class="card-content pt-6">
|
100
|
+
<pre><%= JSON.pretty_generate(@recurring_task.arguments) %></pre>
|
101
|
+
</div>
|
102
|
+
</div>
|
103
|
+
<% end %>
|
104
|
+
|
105
|
+
<% if @recurring_task.description.present? %>
|
106
|
+
<div class="card mt-8 overflow-hidden">
|
107
|
+
<div class="card-header border-b border-t-4 border-t-amber-500 dark:border-t-amber-600">
|
108
|
+
<h3 class="card-title">Description</h3>
|
109
|
+
<p class="card-description">Additional information about this recurring task</p>
|
110
|
+
</div>
|
111
|
+
<div class="card-content pt-6">
|
112
|
+
<pre><%= @recurring_task.description %></pre>
|
113
|
+
</div>
|
114
|
+
</div>
|
115
|
+
<% end %>
|
116
|
+
|
117
|
+
<div class="card mt-8 overflow-hidden">
|
118
|
+
<div class="card-header border-b border-t-4 border-t-zinc-500 dark:border-t-zinc-600">
|
119
|
+
<h3 class="card-title">Next Runs</h3>
|
120
|
+
<p class="card-description">Based on the current schedule</p>
|
121
|
+
</div>
|
122
|
+
<div class="card-content pt-5">
|
123
|
+
<ul class="list-decimal marker:text-sm marker:text-muted-foreground ml-4 space-y-1">
|
124
|
+
<% @recurring_task.next_runs.each do |run_time| %>
|
125
|
+
<li data-date><%= run_time.strftime("%Y-%m-%d %H:%M:%S %Z") %></li>
|
126
|
+
<% end %>
|
127
|
+
</ul>
|
128
|
+
</div>
|
129
|
+
</div>
|
data/bun.lockb
ADDED
Binary file
|
data/config/routes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
SolidQueueDashboard::Engine.routes.draw do
|
2
|
+
resources :jobs, only: [ :index, :show ] do
|
3
|
+
member do
|
4
|
+
post :retry
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
resources :processes, only: [ :index, :show ]
|
9
|
+
|
10
|
+
resources :recurring_tasks, path: "recurring-tasks", only: [ :index, :show ] do
|
11
|
+
member do
|
12
|
+
post :enqueue
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
post "appearance/toggle", to: "appearance#toggle", as: :toggle_appearance
|
17
|
+
root "dashboard#index"
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :title
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@title = "Solid Queue Dashboard"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Decorators
|
3
|
+
class JobDecorator < SimpleDelegator
|
4
|
+
def color
|
5
|
+
Job.status_color(status)
|
6
|
+
end
|
7
|
+
|
8
|
+
def status
|
9
|
+
return @status if defined?(@status)
|
10
|
+
|
11
|
+
@status = if retried?
|
12
|
+
Job::RETRIED
|
13
|
+
elsif failed?
|
14
|
+
Job::FAILED
|
15
|
+
elsif success?
|
16
|
+
Job::SUCCESS
|
17
|
+
elsif scheduled?
|
18
|
+
Job::SCHEDULED
|
19
|
+
else
|
20
|
+
Job::PENDING
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def success?
|
25
|
+
return @success if defined?(@success)
|
26
|
+
@success = finished_at.present? && !failed? && !retried?
|
27
|
+
end
|
28
|
+
|
29
|
+
def retried?
|
30
|
+
return @retried if defined?(@retried)
|
31
|
+
@retried = finished_at.present? && !failed_execution.present? &&
|
32
|
+
(arguments["executions"].to_i > 0 || execution_history.where(scheduled_at: finished_at..).any?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def failed?
|
36
|
+
return @failed if defined?(@failed)
|
37
|
+
@failed = failed_execution.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def scheduled?
|
41
|
+
return @scheduled if defined?(@scheduled)
|
42
|
+
@scheduled = scheduled_at.present? && scheduled_at > Time.current
|
43
|
+
end
|
44
|
+
|
45
|
+
def pending?
|
46
|
+
return @pending if defined?(@pending)
|
47
|
+
@pending = !finished_at.present?
|
48
|
+
end
|
49
|
+
|
50
|
+
def execution_history
|
51
|
+
SolidQueue::Job.where(active_job_id: active_job_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def error_message
|
55
|
+
return @error_message if defined?(@error_message)
|
56
|
+
|
57
|
+
@error_message = failed_execution ?
|
58
|
+
"#{failed_execution.error["exception_class"]}: #{failed_execution.error["message"]}" :
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Decorators
|
3
|
+
class JobsDecorator < SimpleDelegator
|
4
|
+
def with_status(status)
|
5
|
+
case status.to_sym
|
6
|
+
when Job::SUCCESS
|
7
|
+
success
|
8
|
+
when Job::FAILED
|
9
|
+
failed
|
10
|
+
when Job::SCHEDULED
|
11
|
+
scheduled
|
12
|
+
when Job::PENDING
|
13
|
+
pending
|
14
|
+
when Job::RETRIED
|
15
|
+
retried
|
16
|
+
else
|
17
|
+
raise "Invalid status: #{status}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def success
|
22
|
+
where.not(finished_at: nil)
|
23
|
+
.where.not(id: failed)
|
24
|
+
.where.not(id: retried)
|
25
|
+
end
|
26
|
+
|
27
|
+
def scheduled
|
28
|
+
where(finished_at: nil, scheduled_at: Time.current..)
|
29
|
+
end
|
30
|
+
|
31
|
+
def pending
|
32
|
+
where(finished_at: nil, scheduled_at: ..Time.current)
|
33
|
+
.where.not(id: failed)
|
34
|
+
end
|
35
|
+
|
36
|
+
def retried
|
37
|
+
where(finished_at: ..Time.current)
|
38
|
+
.where.not(id: failed)
|
39
|
+
.where(
|
40
|
+
active_job_id: SolidQueue::Job
|
41
|
+
.select(:active_job_id)
|
42
|
+
.group(:active_job_id)
|
43
|
+
.having("COUNT(*) > 1")
|
44
|
+
)
|
45
|
+
.where.not(
|
46
|
+
id: SolidQueue::Job
|
47
|
+
.select("MAX(id)")
|
48
|
+
.group(:active_job_id)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def failure_rate
|
53
|
+
success_count = success.count
|
54
|
+
retried_count = retried.count
|
55
|
+
failed_count = failed.count
|
56
|
+
|
57
|
+
total = success_count + retried_count + failed_count
|
58
|
+
return 0 if total.zero?
|
59
|
+
|
60
|
+
(failed_count + retried_count).to_f / total * 100
|
61
|
+
end
|
62
|
+
|
63
|
+
def each
|
64
|
+
super do |job|
|
65
|
+
yield JobDecorator.new(job)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_a
|
70
|
+
super.map { |job| JobDecorator.new(job) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Decorators
|
3
|
+
class ProcessesDecorator < SimpleDelegator
|
4
|
+
def each
|
5
|
+
super do |job|
|
6
|
+
yield ProcessDecorator.new(job)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
super.map { |job| ProcessDecorator.new(job) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Decorators
|
3
|
+
class RecurringTaskDecorator < SimpleDelegator
|
4
|
+
def type
|
5
|
+
if command.present?
|
6
|
+
RecurringTask::COMMAND
|
7
|
+
elsif class_name.present?
|
8
|
+
RecurringTask::JOB
|
9
|
+
else
|
10
|
+
"Unknown"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_runs(count: 5)
|
15
|
+
cron = Fugit.parse(schedule)
|
16
|
+
return [] unless cron
|
17
|
+
|
18
|
+
cron.next.take(count)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Decorators
|
3
|
+
class RecurringTasksDecorator < SimpleDelegator
|
4
|
+
def with_type(type)
|
5
|
+
case type.to_sym
|
6
|
+
when RecurringTask::JOB
|
7
|
+
where.not(class_name: nil)
|
8
|
+
when RecurringTask::COMMAND
|
9
|
+
where.not(command: nil)
|
10
|
+
else
|
11
|
+
raise "Unknown type: #{type}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
super do |task|
|
17
|
+
yield RecurringTaskDecorator.new(task)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_a
|
22
|
+
super.map { |task| RecurringTaskDecorator.new(task) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace SolidQueueDashboard
|
4
|
+
|
5
|
+
initializer "solid_queue_dashboard.assets.precompile" do |app|
|
6
|
+
app.config.assets.precompile += %w[
|
7
|
+
solid_queue_dashboard/alpine.js
|
8
|
+
solid_queue_dashboard/application.js
|
9
|
+
solid_queue_dashboard/application.css
|
10
|
+
]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Job
|
3
|
+
# Constants
|
4
|
+
SUCCESS = :success
|
5
|
+
RETRIED = :retried
|
6
|
+
FAILED = :failed
|
7
|
+
PENDING = :pending
|
8
|
+
SCHEDULED = :scheduled
|
9
|
+
|
10
|
+
STATUSES = [ SUCCESS, RETRIED, FAILED, SCHEDULED, PENDING ]
|
11
|
+
|
12
|
+
STATUS_COLORS = {
|
13
|
+
SUCCESS => "green",
|
14
|
+
RETRIED => "amber",
|
15
|
+
FAILED => "red",
|
16
|
+
SCHEDULED => "blue",
|
17
|
+
PENDING => "zinc"
|
18
|
+
}
|
19
|
+
|
20
|
+
COMMAND_CLASS_NAME = "SolidQueue::RecurringJob"
|
21
|
+
|
22
|
+
def self.status_color(status)
|
23
|
+
STATUS_COLORS[status] || "zinc"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SolidQueueDashboard
|
2
|
+
module Process
|
3
|
+
# Constants
|
4
|
+
SUPERVISOR = "Supervisor"
|
5
|
+
DISPATCHER = "Dispatcher"
|
6
|
+
WORKER = "Worker"
|
7
|
+
SCHEDULER = "Scheduler"
|
8
|
+
|
9
|
+
KINDS = [ SUPERVISOR, DISPATCHER, WORKER, SCHEDULER ]
|
10
|
+
|
11
|
+
KIND_COLORS = {
|
12
|
+
SUPERVISOR => "blue",
|
13
|
+
DISPATCHER => "green",
|
14
|
+
WORKER => "yellow",
|
15
|
+
SCHEDULER => "purple"
|
16
|
+
}
|
17
|
+
|
18
|
+
HEARTBEAT_DEAD_THRESHOLD = 1.minute
|
19
|
+
|
20
|
+
def self.kind_color(kind)
|
21
|
+
KIND_COLORS[kind] || "zinc"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,8 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "rails"
|
3
4
|
require_relative "solid_queue_dashboard/version"
|
5
|
+
require_relative "solid_queue_dashboard/configuration"
|
6
|
+
require_relative "solid_queue_dashboard/engine"
|
7
|
+
require_relative "solid_queue_dashboard/job"
|
8
|
+
require_relative "solid_queue_dashboard/process"
|
9
|
+
require_relative "solid_queue_dashboard/recurring_task"
|
10
|
+
require_relative "solid_queue_dashboard/decorators/job_decorator"
|
11
|
+
require_relative "solid_queue_dashboard/decorators/jobs_decorator"
|
12
|
+
require_relative "solid_queue_dashboard/decorators/process_decorator"
|
13
|
+
require_relative "solid_queue_dashboard/decorators/processes_decorator"
|
14
|
+
require_relative "solid_queue_dashboard/decorators/recurring_task_decorator"
|
15
|
+
require_relative "solid_queue_dashboard/decorators/recurring_tasks_decorator"
|
4
16
|
|
5
17
|
module SolidQueueDashboard
|
6
18
|
class Error < StandardError; end
|
7
|
-
|
19
|
+
|
20
|
+
def self.job_queue_names
|
21
|
+
SolidQueue::Job.distinct.pluck(:queue_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.job_class_names
|
25
|
+
SolidQueue::Job.distinct.pluck(:class_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.decorate(object)
|
29
|
+
case object
|
30
|
+
when SolidQueue::Job
|
31
|
+
Decorators::JobDecorator.new(object)
|
32
|
+
when SolidQueue::Job.const_get(:ActiveRecord_Relation)
|
33
|
+
Decorators::JobsDecorator.new(object)
|
34
|
+
when SolidQueue::Process
|
35
|
+
Decorators::ProcessDecorator.new(object)
|
36
|
+
when SolidQueue::Process.const_get(:ActiveRecord_Relation)
|
37
|
+
Decorators::ProcessesDecorator.new(object)
|
38
|
+
when SolidQueue::RecurringTask
|
39
|
+
Decorators::RecurringTaskDecorator.new(object)
|
40
|
+
when SolidQueue::RecurringTask.const_get(:ActiveRecord_Relation)
|
41
|
+
Decorators::RecurringTasksDecorator.new(object)
|
42
|
+
else
|
43
|
+
raise Error, "Cannot decorate #{object.class}"
|
44
|
+
end
|
45
|
+
end
|
8
46
|
end
|
data/package.json
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"private": true,
|
3
|
+
"scripts": {
|
4
|
+
"build": "tailwindcss -i ./app/assets/stylesheets/solid_queue_dashboard/tailwind.css -o ./app/assets/stylesheets/solid_queue_dashboard/application.css",
|
5
|
+
"watch": "tailwindcss -i ./app/assets/stylesheets/solid_queue_dashboard/tailwind.css -o ./app/assets/stylesheets/solid_queue_dashboard/application.css --watch"
|
6
|
+
},
|
7
|
+
"devDependencies": {
|
8
|
+
"@tailwindcss/forms": "^0.5.9",
|
9
|
+
"@tailwindcss/typography": "^0.5.15",
|
10
|
+
"tailwindcss": "^3.4.13",
|
11
|
+
"tailwindcss-animate": "^1.0.7"
|
12
|
+
}
|
13
|
+
}
|
data/tailwind.config.js
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
const { fontFamily } = require("tailwindcss/defaultTheme")
|
2
|
+
|
3
|
+
/** @type {import('tailwindcss').Config} */
|
4
|
+
module.exports = {
|
5
|
+
darkMode: ["class"],
|
6
|
+
content: [
|
7
|
+
"./app/views/**/*.html.erb",
|
8
|
+
"./app/helpers/**/*.rb",
|
9
|
+
"./app/javascript/**/*.js",
|
10
|
+
],
|
11
|
+
theme: {
|
12
|
+
container: {
|
13
|
+
center: true,
|
14
|
+
padding: "2rem",
|
15
|
+
screens: {
|
16
|
+
"2xl": "1400px",
|
17
|
+
},
|
18
|
+
},
|
19
|
+
extend: {
|
20
|
+
colors: {
|
21
|
+
border: "hsl(var(--border))",
|
22
|
+
input: "hsl(var(--input))",
|
23
|
+
ring: "hsl(var(--ring))",
|
24
|
+
background: "hsl(var(--background))",
|
25
|
+
foreground: "hsl(var(--foreground))",
|
26
|
+
primary: {
|
27
|
+
DEFAULT: "hsl(var(--primary))",
|
28
|
+
foreground: "hsl(var(--primary-foreground))",
|
29
|
+
},
|
30
|
+
secondary: {
|
31
|
+
DEFAULT: "hsl(var(--secondary))",
|
32
|
+
foreground: "hsl(var(--secondary-foreground))",
|
33
|
+
},
|
34
|
+
destructive: {
|
35
|
+
DEFAULT: "hsl(var(--destructive))",
|
36
|
+
foreground: "hsl(var(--destructive-foreground))",
|
37
|
+
},
|
38
|
+
muted: {
|
39
|
+
DEFAULT: "hsl(var(--muted))",
|
40
|
+
foreground: "hsl(var(--muted-foreground))",
|
41
|
+
},
|
42
|
+
accent: {
|
43
|
+
DEFAULT: "hsl(var(--accent))",
|
44
|
+
foreground: "hsl(var(--accent-foreground))",
|
45
|
+
},
|
46
|
+
popover: {
|
47
|
+
DEFAULT: "hsl(var(--popover))",
|
48
|
+
foreground: "hsl(var(--popover-foreground))",
|
49
|
+
},
|
50
|
+
card: {
|
51
|
+
DEFAULT: "hsl(var(--card))",
|
52
|
+
foreground: "hsl(var(--card-foreground))",
|
53
|
+
},
|
54
|
+
},
|
55
|
+
borderRadius: {
|
56
|
+
lg: `var(--radius)`,
|
57
|
+
md: `calc(var(--radius) - 2px)`,
|
58
|
+
sm: "calc(var(--radius) - 4px)",
|
59
|
+
},
|
60
|
+
opacity: {
|
61
|
+
7.5: "0.075",
|
62
|
+
},
|
63
|
+
keyframes: {
|
64
|
+
"accordion-down": {
|
65
|
+
from: { height: "0" },
|
66
|
+
to: { height: "var(--radix-accordion-content-height)" },
|
67
|
+
},
|
68
|
+
"accordion-up": {
|
69
|
+
from: { height: "var(--radix-accordion-content-height)" },
|
70
|
+
to: { height: "0" },
|
71
|
+
},
|
72
|
+
},
|
73
|
+
animation: {
|
74
|
+
"accordion-down": "accordion-down 0.2s ease-out",
|
75
|
+
"accordion-up": "accordion-up 0.2s ease-out",
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
plugins: [
|
80
|
+
require("tailwindcss-animate"),
|
81
|
+
require("@tailwindcss/forms")({ strategy: "class" }),
|
82
|
+
],
|
83
|
+
}
|