solid_stack_web 0.9.0 → 1.0.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
  SHA256:
3
- metadata.gz: 85093a248316f7f8921636f68bdc08e2053e496eeb85f17bbbd7cddac57e4ed6
4
- data.tar.gz: 3321fa62f08ce3615f733740cfac444d60ed65960b9adc9e4894919416482b46
3
+ metadata.gz: e7932ddf2c50cc9ae63a884c0dabdc0fe4553c9dfb823870d3e1b15b4dc2a3a6
4
+ data.tar.gz: 04d6d15b941d21dfae0d1562095e6cdb4cd67a7e1163efa3bc2d33563df2fb92
5
5
  SHA512:
6
- metadata.gz: 278a4dbde1baa31bb57f58c72d41ea13ef9ddbb83043839228abb4cb0ab06286c4ca954d63ee0c2f2e6914a1b8c24f946731b73afe8db1176ba28a8c157c6b52
7
- data.tar.gz: f6b953b896ba256d03e26f802ab594d9e822fbd114ce68f6b7f673d435fceca8009a9ec2c55ea0f45a6dacfcebb34b9b9c23c64564faa212e00a8450292d95c4
6
+ metadata.gz: 37ac38026dbde5e629d132f18795797f84a7b48b8e19689435b0288f44c57abf73e7dae5aa9e51884021920b4b336160597529b4f87c44cac027e9b88c1293d6
7
+ data.tar.gz: c233686b589092befdcc5b19ca7bfd09a2f10e9f1d95b894f58afff4fb67475ddcae308bf6b65386f9af5479d670256dde1cd02cf3cf2b5466eadcd00a051a2d
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.3-ruby)](https://www.ruby-lang.org)
6
6
  [![codecov](https://codecov.io/gh/eclectic-coding/solid_stack_web/branch/main/graph/badge.svg)](https://codecov.io/gh/eclectic-coding/solid_stack_web)
7
7
 
8
- A mountable Rails engine that provides a unified web dashboard for the full [Solid Stack](https://github.com/rails/solid_queue) — **Solid Queue**, **Solid Cache**, and **Solid Cable** — in a single interface with no asset pipeline dependency and no JavaScript runtime requirement.
8
+ A production-ready operations dashboard for the full Rails Solid Stack. Mount one engine to get deep visibility into **Solid Queue** (job browser, failed job retry, queue controls, recurring tasks, performance stats), **Solid Cache** (entry browser, size distribution, write timeline), and **Solid Cable** (channel browser, message list, purge controls) — with dark mode, CSV export, alert webhooks, and a JSON metrics endpoint, all with no asset pipeline dependency.
9
9
 
10
10
  ## Installation
11
11
 
@@ -41,6 +41,12 @@ This creates `config/initializers/solid_stack_web.rb` with every configuration o
41
41
 
42
42
  ---
43
43
 
44
+ ## Screenshots
45
+
46
+ ![SolidStackWeb dashboard](docs/screenshots/demo.gif)
47
+
48
+ ---
49
+
44
50
  ## Metrics endpoint
45
51
 
46
52
  `GET /metrics` (relative to your mount path) returns a JSON payload suitable for external monitoring tools, uptime checkers, or custom alerting:
@@ -83,6 +89,10 @@ SolidStackWeb.configure do |config|
83
89
  config.authenticate do
84
90
  current_user&.admin?
85
91
  end
92
+
93
+ # Multi-database — pass a connects_to hash when Solid Queue / Cache / Cable
94
+ # live on a separate database from your primary (default: nil, uses primary).
95
+ config.connects_to = { database: { writing: :queue, reading: :queue } }
86
96
  end
87
97
  ```
88
98
 
@@ -100,6 +110,44 @@ link_to "Queue Dashboard", SolidStackWeb.mount_path
100
110
 
101
111
  ---
102
112
 
113
+ ## Security
114
+
115
+ ### Authentication
116
+
117
+ **The dashboard is open to all visitors by default.** Any production deployment must configure an `authenticate` block or the dashboard will be publicly accessible.
118
+
119
+ ```ruby
120
+ SolidStackWeb.configure do |config|
121
+ # Devise
122
+ config.authenticate { current_user&.admin? }
123
+
124
+ # HTTP Basic fallback (used when no authenticate block is set, or when
125
+ # the block returns false/nil and you want a browser credential prompt)
126
+ # Configure via HTTP_BASIC_AUTH_NAME / HTTP_BASIC_AUTH_PASSWORD env vars
127
+ # in your host app, or use a reverse proxy.
128
+ end
129
+ ```
130
+
131
+ If the `authenticate` block returns `false` or `nil`, the engine falls back to HTTP Basic authentication. If no block is configured at all, the dashboard is open.
132
+
133
+ ### Sensitive cache values
134
+
135
+ `allow_value_preview` is `false` by default. Enabling it renders the raw serialised cache value on the entry detail page. Do not enable this if your cache stores session tokens, PII, or other sensitive data.
136
+
137
+ ### CSRF protection
138
+
139
+ All state-mutating actions (job discard, retry, queue pause/resume, cache flush) use form POST requests. Turbo handles CSRF tokens automatically for any standard Rails app with `protect_from_forgery`.
140
+
141
+ ### Rate limiting and network exposure
142
+
143
+ The dashboard is designed to be mounted behind your application's existing authentication. For additional hardening, consider:
144
+
145
+ - Mounting at a non-guessable path (e.g. `at: "/ops/#{Rails.application.credentials.dashboard_token}"`)
146
+ - Restricting access by IP at the reverse-proxy level
147
+ - Applying [Rack::Attack](https://github.com/rack/rack-attack) rules to the mount path
148
+
149
+ ---
150
+
103
151
  ## Solid Queue
104
152
 
105
153
  ### Features
@@ -207,6 +255,36 @@ Filters are preserved when switching between status tabs (Ready / Scheduled / Ru
207
255
  - [turbo-rails](https://github.com/hotwired/turbo-rails) >= 2.0
208
256
  - [importmap-rails](https://github.com/rails/importmap-rails) >= 1.2
209
257
 
258
+ ## Versioning
259
+
260
+ SolidStackWeb follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
261
+
262
+ ### Public API
263
+
264
+ The following are considered stable public API from v1.0.0 onwards — breaking changes to any of these require a major version bump:
265
+
266
+ - The `SolidStackWeb.configure` block and all documented configuration keys
267
+ - The `SolidStackWeb.mount_path` helper
268
+ - The `authenticate` block interface
269
+ - The `GET /metrics` JSON payload shape
270
+ - The `SolidStackWeb::Engine` class and its mount interface
271
+ - The `rails generate solid_stack_web:install` generator
272
+
273
+ ### Not part of the public API
274
+
275
+ The following are internal and may change in any release without notice:
276
+
277
+ - Internal service classes (`CacheStats`, `QueueStats`, etc.)
278
+ - View templates, partial names, and CSS class names
279
+ - Controller and helper internals
280
+ - Private methods on any class
281
+
282
+ ### Deprecation policy
283
+
284
+ When a public API item is renamed or removed, the old interface is deprecated in a **minor** release — it continues to work but issues an `ActiveSupport::Deprecation` warning pointing to the replacement. The old interface is removed in the next **major** release. The [UPGRADING.md](UPGRADING.md) file documents every breaking change and the migration steps.
285
+
286
+ ---
287
+
210
288
  ## Contributing
211
289
 
212
290
  1. Fork the repository
@@ -70,20 +70,9 @@
70
70
  overflow-y: auto;
71
71
  }
72
72
 
73
- .sqw-detail {
74
- display: grid;
75
- grid-template-columns: auto 1fr;
76
- gap: 0.5rem 1.5rem;
77
- font-size: 13px;
78
- margin-bottom: 1.5rem;
79
- }
80
- .sqw-detail__row {
81
- display: contents;
82
- }
83
- .sqw-detail__row dt { color: var(--muted); white-space: nowrap; align-self: start; padding-top: 0.15rem; }
84
- .sqw-detail__row dd { word-break: break-all; }
85
-
86
- .sqw-value-preview { margin-top: 1rem; }
73
+ .sqw-detail-card--mt,
74
+ .sqw-detail-card + .sqw-detail-card,
75
+ .sqw-detail-grid + .sqw-detail-card { margin-top: 1.5rem; }
87
76
 
88
77
  .sqw-value-preview__header {
89
78
  display: flex;
@@ -93,20 +82,6 @@
93
82
  }
94
83
  .sqw-value-preview__header .sqw-section-title { margin-bottom: 0; }
95
84
 
96
- .sqw-value-pre {
97
- font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
98
- font-size: 12px;
99
- background: var(--bg);
100
- border: 1px solid var(--border);
101
- border-radius: var(--radius);
102
- padding: 0.75rem;
103
- overflow-x: auto;
104
- white-space: pre-wrap;
105
- word-break: break-word;
106
- max-height: 500px;
107
- overflow-y: auto;
108
- }
109
-
110
85
  .sqw-value-truncated { font-size: 12px; margin-top: 0.5rem; }
111
86
 
112
87
  .sqw-link { color: var(--primary); text-decoration: none; }
@@ -10,33 +10,30 @@
10
10
  </div>
11
11
  </div>
12
12
 
13
- <dl class="sqw-detail">
14
- <div class="sqw-detail__row">
13
+ <div class="sqw-detail-card sqw-detail-section">
14
+ <h2 class="sqw-section-title">Details</h2>
15
+ <dl class="sqw-dl">
15
16
  <dt>Key</dt>
16
17
  <dd class="sqw-monospace"><%= @entry.key %></dd>
17
- </div>
18
- <div class="sqw-detail__row">
19
18
  <dt>Size</dt>
20
19
  <dd><%= number_to_human_size(@entry.byte_size) %></dd>
21
- </div>
22
- <div class="sqw-detail__row">
23
20
  <dt>Created</dt>
24
21
  <dd class="sqw-muted"><%= local_time(@entry.created_at, format: :long) %></dd>
25
- </div>
26
- </dl>
22
+ </dl>
23
+ </div>
27
24
 
28
- <section class="sqw-value-preview">
25
+ <div class="sqw-detail-card sqw-detail-section">
26
+ <% formatted = SolidStackWeb.allow_value_preview ? format_cache_value(@entry.value) : nil %>
29
27
  <div class="sqw-value-preview__header">
30
28
  <h2 class="sqw-section-title">Value</h2>
31
- <% if SolidStackWeb.allow_value_preview %>
32
- <% formatted = format_cache_value(@entry.value) %>
29
+ <% if formatted %>
33
30
  <span class="sqw-badge sqw-badge--queue"><%= formatted[:label] %></span>
34
31
  <% end %>
35
32
  </div>
36
- <% if SolidStackWeb.allow_value_preview %>
33
+ <% if formatted %>
37
34
  <% content = formatted[:content] %>
38
35
  <% truncated = content.length > 4096 %>
39
- <pre class="sqw-value-pre"><%= truncated ? content[0, 4096] : content %></pre>
36
+ <pre class="sqw-code-block"><%= truncated ? content[0, 4096] : content %></pre>
40
37
  <% if truncated %>
41
38
  <p class="sqw-muted sqw-value-truncated">Showing first 4 KB of <%= number_to_human_size(@entry.byte_size) %> total.</p>
42
39
  <% end %>
@@ -45,4 +42,4 @@
45
42
  <p>Value preview is disabled. Set <code>config.allow_value_preview = true</code> in your initializer to enable.</p>
46
43
  </div>
47
44
  <% end %>
48
- </section>
45
+ </div>
@@ -45,7 +45,7 @@
45
45
  </div>
46
46
  </div>
47
47
 
48
- <div class="sqw-detail-card sqw-detail-section" style="margin-top: 1.5rem;">
48
+ <div class="sqw-detail-card sqw-detail-section sqw-detail-card--mt">
49
49
  <h2 class="sqw-section-title">Arguments</h2>
50
50
  <%= form_with url: failed_job_arguments_path(@execution), method: :patch do |f| %>
51
51
  <%= f.text_area :arguments, value: @arguments, rows: 12,
Binary file
@@ -25,6 +25,10 @@ module SolidStackWeb
25
25
  end
26
26
  end
27
27
 
28
+ initializer "solid_stack_web.deprecator" do |app|
29
+ app.deprecators[:solid_stack_web] = SolidStackWeb.deprecator
30
+ end
31
+
28
32
  initializer "solid_stack_web.pagy" do |app|
29
33
  app.config.after_initialize do
30
34
  Pagy::OPTIONS[:limit] = SolidStackWeb.page_size
@@ -1,3 +1,3 @@
1
1
  module SolidStackWeb
2
- VERSION = "0.9.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -74,5 +74,27 @@ module SolidStackWeb
74
74
  @authenticate = block if block_given?
75
75
  @authenticate
76
76
  end
77
+
78
+ def deprecator
79
+ @deprecator ||= ActiveSupport::Deprecation.new("1.0", "SolidStackWeb")
80
+ end
81
+
82
+ private
83
+
84
+ # Define a deprecated writer for a renamed config key.
85
+ # The old writer issues a deprecation warning and forwards to the new key.
86
+ #
87
+ # Usage (add entries here as keys are renamed before 1.0):
88
+ # deprecated_config :old_key, :new_key
89
+ def deprecated_config(old_key, new_key)
90
+ singleton_class.define_method(:"#{old_key}=") do |value|
91
+ deprecator.warn(
92
+ "config.#{old_key}= is deprecated and will be removed in SolidStackWeb 1.0. " \
93
+ "Use config.#{new_key}= instead.",
94
+ caller_locations
95
+ )
96
+ public_send(:"#{new_key}=", value)
97
+ end
98
+ end
77
99
  end
78
100
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_stack_web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -121,9 +121,14 @@ dependencies:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
123
  version: '3.0'
124
- description: Mount SolidStackWeb in any Rails app using the Solid Stack to get a single
125
- dashboard covering Solid Queue job monitoring, Solid Cache statistics, and Solid
126
- Cable connection observability — all without leaving your app.
124
+ description: SolidStackWeb is a mountable Rails engine that provides a production-ready
125
+ operations dashboard for the full Solid Stack. It covers Solid Queue (job browser,
126
+ failed job retry with inline argument editing, queue pause/resume, recurring tasks,
127
+ performance stats, CSV export, and alert webhooks), Solid Cache (entry browser,
128
+ size distribution, 24-hour write timeline, and optional value preview), and Solid
129
+ Cable (channel browser, per-channel message list, and purge controls). Ships with
130
+ dark mode, Turbo Stream responses, a JSON metrics endpoint, and no asset pipeline
131
+ dependency.
127
132
  email:
128
133
  - eclectic-coding@users.noreply.github.com
129
134
  executables: []
@@ -212,6 +217,7 @@ files:
212
217
  - app/views/solid_stack_web/stats/index.html.erb
213
218
  - config/importmap.rb
214
219
  - config/routes.rb
220
+ - docs/screenshots/demo.gif
215
221
  - lib/generators/solid_stack_web/install/install_generator.rb
216
222
  - lib/generators/solid_stack_web/install/templates/initializer.rb
217
223
  - lib/solid_stack_web.rb