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 +4 -4
- data/README.md +79 -1
- data/app/assets/stylesheets/solid_stack_web/_09_detail.css +3 -28
- data/app/views/solid_stack_web/cache_entries/show.html.erb +11 -14
- data/app/views/solid_stack_web/failed_jobs/show.html.erb +1 -1
- data/docs/screenshots/demo.gif +0 -0
- data/lib/solid_stack_web/engine.rb +4 -0
- data/lib/solid_stack_web/version.rb +1 -1
- data/lib/solid_stack_web.rb +22 -0
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7932ddf2c50cc9ae63a884c0dabdc0fe4553c9dfb823870d3e1b15b4dc2a3a6
|
|
4
|
+
data.tar.gz: 04d6d15b941d21dfae0d1562095e6cdb4cd67a7e1163efa3bc2d33563df2fb92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37ac38026dbde5e629d132f18795797f84a7b48b8e19689435b0288f44c57abf73e7dae5aa9e51884021920b4b336160597529b4f87c44cac027e9b88c1293d6
|
|
7
|
+
data.tar.gz: c233686b589092befdcc5b19ca7bfd09a2f10e9f1d95b894f58afff4fb67475ddcae308bf6b65386f9af5479d670256dde1cd02cf3cf2b5466eadcd00a051a2d
|
data/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.ruby-lang.org)
|
|
6
6
|
[](https://codecov.io/gh/eclectic-coding/solid_stack_web)
|
|
7
7
|
|
|
8
|
-
A
|
|
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
|
+

|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
<
|
|
14
|
-
<
|
|
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
|
-
</
|
|
26
|
-
</
|
|
22
|
+
</dl>
|
|
23
|
+
</div>
|
|
27
24
|
|
|
28
|
-
<
|
|
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
|
|
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
|
|
33
|
+
<% if formatted %>
|
|
37
34
|
<% content = formatted[:content] %>
|
|
38
35
|
<% truncated = content.length > 4096 %>
|
|
39
|
-
<pre class="sqw-
|
|
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
|
-
</
|
|
45
|
+
</div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
47
|
|
|
48
|
-
<div class="sqw-detail-card sqw-detail-section
|
|
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
|
data/lib/solid_stack_web.rb
CHANGED
|
@@ -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.
|
|
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:
|
|
125
|
-
dashboard
|
|
126
|
-
|
|
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
|