flightdeck 0.5.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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.md +190 -0
  4. data/app/assets/flightdeck/flightdeck-00403457c5d9.css +3 -0
  5. data/app/assets/flightdeck/flightdeck-c02e38040ddb.js +414 -0
  6. data/app/assets/flightdeck/manifest.json +16 -0
  7. data/app/controllers/concerns/flightdeck/toasts.rb +25 -0
  8. data/app/controllers/flightdeck/application_controller.rb +96 -0
  9. data/app/controllers/flightdeck/assets_controller.rb +39 -0
  10. data/app/controllers/flightdeck/jobs/actions_controller.rb +167 -0
  11. data/app/controllers/flightdeck/jobs/discards_controller.rb +44 -0
  12. data/app/controllers/flightdeck/jobs/retries_controller.rb +23 -0
  13. data/app/controllers/flightdeck/jobs_controller.rb +39 -0
  14. data/app/controllers/flightdeck/overview_controller.rb +11 -0
  15. data/app/controllers/flightdeck/processes_controller.rb +49 -0
  16. data/app/controllers/flightdeck/queues_controller.rb +51 -0
  17. data/app/controllers/flightdeck/recurring_tasks_controller.rb +46 -0
  18. data/app/helpers/flightdeck/application_helper.rb +68 -0
  19. data/app/helpers/flightdeck/infrastructure_helper.rb +24 -0
  20. data/app/helpers/flightdeck/jobs_helper.rb +136 -0
  21. data/app/helpers/flightdeck/metrics_helper.rb +49 -0
  22. data/app/models/flightdeck/arguments_preview.rb +95 -0
  23. data/app/models/flightdeck/bulk_action.rb +83 -0
  24. data/app/models/flightdeck/cache.rb +51 -0
  25. data/app/models/flightdeck/cron_schedule.rb +102 -0
  26. data/app/models/flightdeck/error_summary.rb +64 -0
  27. data/app/models/flightdeck/grouped_failures.rb +24 -0
  28. data/app/models/flightdeck/job_detail.rb +163 -0
  29. data/app/models/flightdeck/job_row.rb +93 -0
  30. data/app/models/flightdeck/jobs_query.rb +241 -0
  31. data/app/models/flightdeck/metrics/bar_chart.rb +49 -0
  32. data/app/models/flightdeck/metrics/chart.rb +111 -0
  33. data/app/models/flightdeck/metrics/line_chart.rb +67 -0
  34. data/app/models/flightdeck/metrics/series.rb +165 -0
  35. data/app/models/flightdeck/metrics/sparkline.rb +45 -0
  36. data/app/models/flightdeck/metrics/time_bucket.rb +95 -0
  37. data/app/models/flightdeck/overview.rb +217 -0
  38. data/app/models/flightdeck/process_registry.rb +108 -0
  39. data/app/models/flightdeck/queue_stats.rb +88 -0
  40. data/app/models/flightdeck/recurring_catalog.rb +116 -0
  41. data/app/views/flightdeck/jobs/_failed_row.html.erb +33 -0
  42. data/app/views/flightdeck/jobs/_filter_bar.html.erb +29 -0
  43. data/app/views/flightdeck/jobs/_list.html.erb +89 -0
  44. data/app/views/flightdeck/jobs/_pager.html.erb +22 -0
  45. data/app/views/flightdeck/jobs/_row.html.erb +23 -0
  46. data/app/views/flightdeck/jobs/_state_tabs.html.erb +10 -0
  47. data/app/views/flightdeck/jobs/actions/create.turbo_stream.erb +18 -0
  48. data/app/views/flightdeck/jobs/index.html.erb +13 -0
  49. data/app/views/flightdeck/jobs/show.html.erb +100 -0
  50. data/app/views/flightdeck/metrics/_bar_chart.html.erb +34 -0
  51. data/app/views/flightdeck/metrics/_line_chart.html.erb +38 -0
  52. data/app/views/flightdeck/metrics/_range_control.html.erb +7 -0
  53. data/app/views/flightdeck/metrics/_sparkline.html.erb +9 -0
  54. data/app/views/flightdeck/overview/_completion.html.erb +19 -0
  55. data/app/views/flightdeck/overview/_failures.html.erb +28 -0
  56. data/app/views/flightdeck/overview/_fleet.html.erb +21 -0
  57. data/app/views/flightdeck/overview/_queues.html.erb +41 -0
  58. data/app/views/flightdeck/overview/_throughput.html.erb +23 -0
  59. data/app/views/flightdeck/overview/_tiles.html.erb +14 -0
  60. data/app/views/flightdeck/overview/index.html.erb +54 -0
  61. data/app/views/flightdeck/processes/_fleet.html.erb +54 -0
  62. data/app/views/flightdeck/processes/_row.html.erb +28 -0
  63. data/app/views/flightdeck/processes/index.html.erb +9 -0
  64. data/app/views/flightdeck/processes/prune.turbo_stream.erb +15 -0
  65. data/app/views/flightdeck/queues/_cards.html.erb +46 -0
  66. data/app/views/flightdeck/queues/index.html.erb +9 -0
  67. data/app/views/flightdeck/queues/update.turbo_stream.erb +15 -0
  68. data/app/views/flightdeck/recurring_tasks/_table.html.erb +53 -0
  69. data/app/views/flightdeck/recurring_tasks/index.html.erb +9 -0
  70. data/app/views/flightdeck/recurring_tasks/run.turbo_stream.erb +15 -0
  71. data/app/views/flightdeck/shared/_toast.html.erb +7 -0
  72. data/app/views/layouts/flightdeck/application.html.erb +110 -0
  73. data/config/routes.rb +40 -0
  74. data/lib/flightdeck/assets.rb +78 -0
  75. data/lib/flightdeck/configuration.rb +80 -0
  76. data/lib/flightdeck/engine.rb +31 -0
  77. data/lib/flightdeck/version.rb +5 -0
  78. data/lib/flightdeck.rb +31 -0
  79. metadata +150 -0
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "pathname"
5
+
6
+ module Flightdeck
7
+ # In-memory view of the precompiled asset manifest committed under
8
+ # app/assets/flightdeck/. Loaded lazily and memoized: the gem must boot
9
+ # cleanly in a checkout where `rake assets:build` has not run yet.
10
+ module Assets
11
+ CONTENT_TYPES = {
12
+ ".css" => "text/css; charset=utf-8",
13
+ ".js" => "text/javascript; charset=utf-8"
14
+ }.freeze
15
+
16
+ LOGICAL_NAMES = %w[flightdeck.css flightdeck.js].freeze
17
+
18
+ class << self
19
+ def root
20
+ @root ||= Pathname.new(File.expand_path("../../app/assets/flightdeck", __dir__))
21
+ end
22
+
23
+ def manifest
24
+ return @manifest if defined?(@manifest) && @manifest
25
+
26
+ @manifest = load_manifest
27
+ end
28
+
29
+ # Reverse index: digested filename => entry. The only lookup the
30
+ # controller performs, so a request can never name a file we did not
31
+ # build.
32
+ def by_digested_name
33
+ @by_digested_name ||= manifest.each_with_object({}) do |(logical, entry), index|
34
+ index[entry["file"]] = entry.merge("logical" => logical)
35
+ end.freeze
36
+ end
37
+
38
+ def digested_name(logical)
39
+ entry = manifest[logical.to_s]
40
+ entry && entry["file"]
41
+ end
42
+
43
+ def find(digested_name)
44
+ by_digested_name[digested_name.to_s]
45
+ end
46
+
47
+ def read(digested_name)
48
+ entry = find(digested_name)
49
+ return nil unless entry
50
+
51
+ path = root.join(entry["file"])
52
+ return nil unless path.file?
53
+
54
+ path.binread
55
+ end
56
+
57
+ def content_type_for(file)
58
+ CONTENT_TYPES.fetch(File.extname(file), "application/octet-stream")
59
+ end
60
+
61
+ def reload!
62
+ remove_instance_variable(:@manifest) if defined?(@manifest)
63
+ remove_instance_variable(:@by_digested_name) if defined?(@by_digested_name)
64
+ manifest
65
+ end
66
+
67
+ private
68
+ def load_manifest
69
+ path = root.join("manifest.json")
70
+ return {} unless path.file?
71
+
72
+ JSON.parse(path.read).freeze
73
+ rescue JSON::ParserError
74
+ {}
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flightdeck
4
+ class Configuration
5
+ attr_accessor :base_controller_class,
6
+ :http_basic,
7
+ :poll_interval,
8
+ :chart_poll_interval,
9
+ :per_page,
10
+ :count_cap,
11
+ :bulk_action_limit,
12
+ :chart_cache_ttl,
13
+ :display_timezone,
14
+ :backtrace_lines
15
+
16
+ def initialize
17
+ @base_controller_class = nil
18
+ @http_basic = nil
19
+ @poll_interval = 5.seconds
20
+ @chart_poll_interval = 30.seconds
21
+ @per_page = 25
22
+ @count_cap = 500_000
23
+ @bulk_action_limit = 1_000
24
+ @chart_cache_ttl = 30.seconds
25
+ @display_timezone = "UTC"
26
+ @backtrace_lines = 50
27
+ end
28
+
29
+ # Resolved fresh on every call so that credentials rotated in the
30
+ # environment (or in Rails credentials) take effect without a restart,
31
+ # and so that nothing touches Rails.application.credentials at boot.
32
+ #
33
+ # Returns a { username:, password: } hash, or nil when unconfigured.
34
+ def resolve_http_basic
35
+ from_explicit || from_env || from_credentials
36
+ end
37
+
38
+ private
39
+ def from_explicit
40
+ normalize(http_basic)
41
+ end
42
+
43
+ def from_env
44
+ normalize(username: ENV["FLIGHTDECK_USERNAME"], password: ENV["FLIGHTDECK_PASSWORD"])
45
+ end
46
+
47
+ def from_credentials
48
+ return nil unless defined?(Rails) && Rails.application
49
+
50
+ normalize(Rails.application.credentials.flightdeck)
51
+ rescue StandardError
52
+ nil
53
+ end
54
+
55
+ def normalize(source)
56
+ return nil if source.nil?
57
+
58
+ source = source.call if source.respond_to?(:call)
59
+ return nil if source.nil?
60
+
61
+ username = fetch(source, :username)
62
+ password = fetch(source, :password)
63
+ return nil if username.nil? || password.nil?
64
+
65
+ username = username.to_s
66
+ password = password.to_s
67
+ return nil if username.empty? || password.empty?
68
+
69
+ { username: username, password: password }
70
+ end
71
+
72
+ def fetch(source, key)
73
+ if source.respond_to?(:[])
74
+ source[key] || source[key.to_s]
75
+ elsif source.respond_to?(key)
76
+ source.public_send(key)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "action_controller/railtie"
5
+ require "action_view/railtie"
6
+ require "solid_queue"
7
+
8
+ module Flightdeck
9
+ class Engine < ::Rails::Engine
10
+ isolate_namespace Flightdeck
11
+
12
+ # Engine-local middleware: wraps only requests routed into the mount, so
13
+ # Flightdeck has cookies, a session and flash even when the host app is
14
+ # `config.api_only = true` and carries none of them.
15
+ middleware.use ActionDispatch::Cookies
16
+ middleware.use ActionDispatch::Session::CookieStore,
17
+ key: "_flightdeck_session",
18
+ same_site: :lax,
19
+ httponly: true
20
+ middleware.use ActionDispatch::Flash
21
+
22
+ config.flightdeck = Flightdeck.config
23
+
24
+ # Flightdeck speaks Turbo Streams but does not depend on turbo-rails: the
25
+ # engine ships its own Turbo build. Registering the type is all the server
26
+ # side actually needs, and the host may well have registered it already.
27
+ initializer "flightdeck.mime_types" do
28
+ Mime::Type.register "text/vnd.turbo-stream.html", :turbo_stream unless Mime[:turbo_stream]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flightdeck
4
+ VERSION = "0.5.0"
5
+ end
data/lib/flightdeck.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/numeric/time"
5
+ require "active_support/core_ext/object/blank"
6
+ require "active_support/core_ext/string/inflections"
7
+
8
+ require "flightdeck/version"
9
+ require "flightdeck/configuration"
10
+ require "flightdeck/assets"
11
+
12
+ module Flightdeck
13
+ class << self
14
+ def config
15
+ @config ||= Configuration.new
16
+ end
17
+
18
+ def configure
19
+ yield config
20
+ end
21
+
22
+ # Resolved at controller-definition time. Zeitwerk loads the engine's
23
+ # controllers lazily, so host configuration set in an initializer wins.
24
+ def base_controller_class
25
+ name = config.base_controller_class
26
+ name.presence ? name.to_s.constantize : ActionController::Base
27
+ end
28
+ end
29
+ end
30
+
31
+ require "flightdeck/engine"
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flightdeck
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Carl Mercier
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: solid_queue
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ description: 'Flightdeck is a mountable Rails engine that gives Solid Queue a fast,
41
+ polished dashboard: jobs, failures, queues, processes, recurring tasks and live
42
+ charts. Works in API-only hosts and ships its own precompiled assets.'
43
+ email:
44
+ - carl@carlmercier.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - MIT-LICENSE
50
+ - README.md
51
+ - app/assets/flightdeck/flightdeck-00403457c5d9.css
52
+ - app/assets/flightdeck/flightdeck-c02e38040ddb.js
53
+ - app/assets/flightdeck/manifest.json
54
+ - app/controllers/concerns/flightdeck/toasts.rb
55
+ - app/controllers/flightdeck/application_controller.rb
56
+ - app/controllers/flightdeck/assets_controller.rb
57
+ - app/controllers/flightdeck/jobs/actions_controller.rb
58
+ - app/controllers/flightdeck/jobs/discards_controller.rb
59
+ - app/controllers/flightdeck/jobs/retries_controller.rb
60
+ - app/controllers/flightdeck/jobs_controller.rb
61
+ - app/controllers/flightdeck/overview_controller.rb
62
+ - app/controllers/flightdeck/processes_controller.rb
63
+ - app/controllers/flightdeck/queues_controller.rb
64
+ - app/controllers/flightdeck/recurring_tasks_controller.rb
65
+ - app/helpers/flightdeck/application_helper.rb
66
+ - app/helpers/flightdeck/infrastructure_helper.rb
67
+ - app/helpers/flightdeck/jobs_helper.rb
68
+ - app/helpers/flightdeck/metrics_helper.rb
69
+ - app/models/flightdeck/arguments_preview.rb
70
+ - app/models/flightdeck/bulk_action.rb
71
+ - app/models/flightdeck/cache.rb
72
+ - app/models/flightdeck/cron_schedule.rb
73
+ - app/models/flightdeck/error_summary.rb
74
+ - app/models/flightdeck/grouped_failures.rb
75
+ - app/models/flightdeck/job_detail.rb
76
+ - app/models/flightdeck/job_row.rb
77
+ - app/models/flightdeck/jobs_query.rb
78
+ - app/models/flightdeck/metrics/bar_chart.rb
79
+ - app/models/flightdeck/metrics/chart.rb
80
+ - app/models/flightdeck/metrics/line_chart.rb
81
+ - app/models/flightdeck/metrics/series.rb
82
+ - app/models/flightdeck/metrics/sparkline.rb
83
+ - app/models/flightdeck/metrics/time_bucket.rb
84
+ - app/models/flightdeck/overview.rb
85
+ - app/models/flightdeck/process_registry.rb
86
+ - app/models/flightdeck/queue_stats.rb
87
+ - app/models/flightdeck/recurring_catalog.rb
88
+ - app/views/flightdeck/jobs/_failed_row.html.erb
89
+ - app/views/flightdeck/jobs/_filter_bar.html.erb
90
+ - app/views/flightdeck/jobs/_list.html.erb
91
+ - app/views/flightdeck/jobs/_pager.html.erb
92
+ - app/views/flightdeck/jobs/_row.html.erb
93
+ - app/views/flightdeck/jobs/_state_tabs.html.erb
94
+ - app/views/flightdeck/jobs/actions/create.turbo_stream.erb
95
+ - app/views/flightdeck/jobs/index.html.erb
96
+ - app/views/flightdeck/jobs/show.html.erb
97
+ - app/views/flightdeck/metrics/_bar_chart.html.erb
98
+ - app/views/flightdeck/metrics/_line_chart.html.erb
99
+ - app/views/flightdeck/metrics/_range_control.html.erb
100
+ - app/views/flightdeck/metrics/_sparkline.html.erb
101
+ - app/views/flightdeck/overview/_completion.html.erb
102
+ - app/views/flightdeck/overview/_failures.html.erb
103
+ - app/views/flightdeck/overview/_fleet.html.erb
104
+ - app/views/flightdeck/overview/_queues.html.erb
105
+ - app/views/flightdeck/overview/_throughput.html.erb
106
+ - app/views/flightdeck/overview/_tiles.html.erb
107
+ - app/views/flightdeck/overview/index.html.erb
108
+ - app/views/flightdeck/processes/_fleet.html.erb
109
+ - app/views/flightdeck/processes/_row.html.erb
110
+ - app/views/flightdeck/processes/index.html.erb
111
+ - app/views/flightdeck/processes/prune.turbo_stream.erb
112
+ - app/views/flightdeck/queues/_cards.html.erb
113
+ - app/views/flightdeck/queues/index.html.erb
114
+ - app/views/flightdeck/queues/update.turbo_stream.erb
115
+ - app/views/flightdeck/recurring_tasks/_table.html.erb
116
+ - app/views/flightdeck/recurring_tasks/index.html.erb
117
+ - app/views/flightdeck/recurring_tasks/run.turbo_stream.erb
118
+ - app/views/flightdeck/shared/_toast.html.erb
119
+ - app/views/layouts/flightdeck/application.html.erb
120
+ - config/routes.rb
121
+ - lib/flightdeck.rb
122
+ - lib/flightdeck/assets.rb
123
+ - lib/flightdeck/configuration.rb
124
+ - lib/flightdeck/engine.rb
125
+ - lib/flightdeck/version.rb
126
+ homepage: https://github.com/cmer/solid_queue-flightdeck
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ homepage_uri: https://github.com/cmer/solid_queue-flightdeck
131
+ source_code_uri: https://github.com/cmer/solid_queue-flightdeck
132
+ changelog_uri: https://github.com/cmer/solid_queue-flightdeck/blob/main/CHANGELOG.md
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '3.1'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 4.0.10
148
+ specification_version: 4
149
+ summary: A beautiful, featureful dashboard for Solid Queue.
150
+ test_files: []