rails_claude_skills 0.1.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 +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
- data/.github/ISSUE_TEMPLATE/config.yml +11 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
- data/.github/ISSUE_TEMPLATE/question.yml +90 -0
- data/.github/dependabot.yml +19 -0
- data/.github/workflows/ci.yml +77 -0
- data/.github/workflows/release.yml +66 -0
- data/.rubocop.yml +52 -0
- data/CHANGELOG.md +94 -0
- data/CLAUDE.md +332 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +580 -0
- data/LICENSE.txt +21 -0
- data/README.md +544 -0
- data/Rakefile +8 -0
- data/lib/generators/claude/agent/agent_generator.rb +71 -0
- data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
- data/lib/generators/claude/command/command_generator.rb +50 -0
- data/lib/generators/claude/command/templates/command.md.tt +28 -0
- data/lib/generators/claude/commands_library/create-pr.md +27 -0
- data/lib/generators/claude/commands_library/dbchange.md +19 -0
- data/lib/generators/claude/commands_library/quality.md +20 -0
- data/lib/generators/claude/commands_library/stimulus.md +19 -0
- data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
- data/lib/generators/claude/install/install_generator.rb +211 -0
- data/lib/generators/claude/install/templates/README.md.tt +59 -0
- data/lib/generators/claude/install/templates/USAGE +28 -0
- data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
- data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
- data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
- data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
- data/lib/generators/claude/rule/rule_generator.rb +175 -0
- data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
- data/lib/generators/claude/rules_library/code-style.md +37 -0
- data/lib/generators/claude/rules_library/database.md +47 -0
- data/lib/generators/claude/rules_library/hotwire.md +56 -0
- data/lib/generators/claude/rules_library/security.md +54 -0
- data/lib/generators/claude/rules_library/testing.md +47 -0
- data/lib/generators/claude/skill/skill_generator.rb +196 -0
- data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
- data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
- data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
- data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
- data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
- data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
- data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
- data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
- data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
- data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
- data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
- data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
- data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
- data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
- data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
- data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
- data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
- data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
- data/lib/generators/claude/views/views_generator.rb +113 -0
- data/lib/rails_claude_skills/railtie.rb +16 -0
- data/lib/rails_claude_skills/version.rb +5 -0
- data/lib/rails_claude_skills.rb +27 -0
- data/sig/rails_claude_skills.rbs +4 -0
- metadata +199 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: jobs
|
|
3
|
+
category: reference
|
|
4
|
+
description: Mission Control Jobs setup and authentication patterns
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Mission Control Jobs - Complete Setup Guide
|
|
8
|
+
|
|
9
|
+
Mission Control Jobs provides a production-ready web dashboard for monitoring and managing SolidQueue background jobs. This guide covers complete setup for development through production deployment with team access.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Add Gem
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
# Gemfile
|
|
17
|
+
gem "mission_control-jobs"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Mount Engine with Authentication
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
# config/routes.rb
|
|
28
|
+
Rails.application.routes.draw do
|
|
29
|
+
# Production: Require admin authentication
|
|
30
|
+
if Rails.env.production?
|
|
31
|
+
authenticate :user, ->(user) { user.admin? } do
|
|
32
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
# Development/Staging: Open access or HTTP Basic Auth
|
|
36
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. Configure (Optional)
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# config/initializers/mission_control.rb
|
|
45
|
+
MissionControl::Jobs.configure do |config|
|
|
46
|
+
# Job retention periods
|
|
47
|
+
config.finished_jobs_retention_period = 14.days # Default: 7 days
|
|
48
|
+
config.failed_jobs_retention_period = 90.days # Default: 30 days
|
|
49
|
+
|
|
50
|
+
# Filter sensitive arguments from dashboard display
|
|
51
|
+
config.filter_parameters = [:password, :token, :secret, :api_key]
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 4. Access Dashboard
|
|
56
|
+
|
|
57
|
+
Visit `http://localhost:3000/jobs` in your browser (development) or `https://yourapp.com/jobs` (production).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Production Authentication Patterns
|
|
62
|
+
|
|
63
|
+
### Pattern 1: Devise Admin Users (Recommended)
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# config/routes.rb
|
|
67
|
+
Rails.application.routes.draw do
|
|
68
|
+
authenticate :user, ->(user) { user.admin? } do
|
|
69
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Requirements:**
|
|
75
|
+
- User model with `admin?` method or `admin` boolean field
|
|
76
|
+
- Devise authentication already configured
|
|
77
|
+
|
|
78
|
+
**Example User Model:**
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# app/models/user.rb
|
|
82
|
+
class User < ApplicationRecord
|
|
83
|
+
devise :database_authenticatable, :registerable,
|
|
84
|
+
:recoverable, :rememberable, :validatable
|
|
85
|
+
|
|
86
|
+
# Option 1: Boolean field
|
|
87
|
+
def admin?
|
|
88
|
+
admin # Assumes `admin` boolean column exists
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Option 2: Role-based
|
|
92
|
+
enum role: { user: 0, admin: 1, superadmin: 2 }
|
|
93
|
+
|
|
94
|
+
def admin?
|
|
95
|
+
admin? || superadmin?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Pattern 2: Custom Authentication Logic
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
# config/routes.rb
|
|
104
|
+
Rails.application.routes.draw do
|
|
105
|
+
authenticate :user, ->(user) { user.can_access_mission_control? } do
|
|
106
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# app/models/user.rb
|
|
111
|
+
class User < ApplicationRecord
|
|
112
|
+
def can_access_mission_control?
|
|
113
|
+
admin? || role == "operations" || email.end_with?("@yourcompany.com")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Pattern 3: HTTP Basic Auth (Staging/Internal Tools)
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# config/routes.rb
|
|
122
|
+
Rails.application.routes.draw do
|
|
123
|
+
# Add constraint for HTTP Basic Auth
|
|
124
|
+
constraints(->(req) { authenticate_mission_control(req) }) do
|
|
125
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# config/application.rb or initializer
|
|
130
|
+
def authenticate_mission_control(request)
|
|
131
|
+
return true if Rails.env.development?
|
|
132
|
+
|
|
133
|
+
authenticate_or_request_with_http_basic do |username, password|
|
|
134
|
+
username == ENV['MISSION_CONTROL_USERNAME'] &&
|
|
135
|
+
password == ENV['MISSION_CONTROL_PASSWORD']
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Set environment variables:**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# .env or production secrets
|
|
144
|
+
MISSION_CONTROL_USERNAME=admin
|
|
145
|
+
MISSION_CONTROL_PASSWORD=secure_random_password_here
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Pattern 4: IP Whitelist (Internal Networks)
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# config/routes.rb
|
|
152
|
+
Rails.application.routes.draw do
|
|
153
|
+
constraints(->(req) { internal_ip?(req.remote_ip) }) do
|
|
154
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# config/application.rb
|
|
159
|
+
def internal_ip?(ip)
|
|
160
|
+
allowed_ips = ENV.fetch('MISSION_CONTROL_IPS', '').split(',')
|
|
161
|
+
allowed_ips.include?(ip) || ip.start_with?('10.', '192.168.')
|
|
162
|
+
end
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Pattern 5: Multi-Environment Configuration
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
# config/routes.rb
|
|
169
|
+
Rails.application.routes.draw do
|
|
170
|
+
case Rails.env
|
|
171
|
+
when "production"
|
|
172
|
+
# Production: Require admin user
|
|
173
|
+
authenticate :user, ->(user) { user.admin? } do
|
|
174
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
175
|
+
end
|
|
176
|
+
when "staging"
|
|
177
|
+
# Staging: HTTP Basic Auth
|
|
178
|
+
constraints(->(req) { authenticate_basic(req) }) do
|
|
179
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
# Development: Open access
|
|
183
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Team Access Management
|
|
191
|
+
|
|
192
|
+
### Granting Admin Access
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
# Rails console (production)
|
|
196
|
+
rails console
|
|
197
|
+
|
|
198
|
+
# Grant admin access to user
|
|
199
|
+
user = User.find_by(email: "teammate@company.com")
|
|
200
|
+
user.update!(admin: true)
|
|
201
|
+
|
|
202
|
+
# Or using role enum
|
|
203
|
+
user.update!(role: :admin)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Bulk Admin Creation
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
# db/seeds.rb or migration
|
|
210
|
+
admin_emails = [
|
|
211
|
+
"ops_lead@company.com",
|
|
212
|
+
"dev_lead@company.com",
|
|
213
|
+
"support_manager@company.com"
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
admin_emails.each do |email|
|
|
217
|
+
user = User.find_or_create_by(email: email)
|
|
218
|
+
user.update!(admin: true)
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Team Roles Pattern
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
# app/models/user.rb
|
|
226
|
+
class User < ApplicationRecord
|
|
227
|
+
enum role: {
|
|
228
|
+
user: 0,
|
|
229
|
+
developer: 1,
|
|
230
|
+
operations: 2,
|
|
231
|
+
admin: 3
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
def can_access_jobs_dashboard?
|
|
235
|
+
developer? || operations? || admin?
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# config/routes.rb
|
|
240
|
+
authenticate :user, ->(user) { user.can_access_jobs_dashboard? } do
|
|
241
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
242
|
+
end
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Dashboard Features & Usage
|
|
248
|
+
|
|
249
|
+
### Jobs Overview Tab
|
|
250
|
+
|
|
251
|
+
**Features:**
|
|
252
|
+
- View all jobs across all queues
|
|
253
|
+
- Filter by status: pending, running, finished, failed
|
|
254
|
+
- Real-time updates (auto-refresh)
|
|
255
|
+
- Queue performance metrics
|
|
256
|
+
|
|
257
|
+
**Common Operations:**
|
|
258
|
+
- Search jobs by class name
|
|
259
|
+
- Filter by date range
|
|
260
|
+
- Sort by created/finished time
|
|
261
|
+
|
|
262
|
+
### Queues Tab
|
|
263
|
+
|
|
264
|
+
**Metrics Displayed:**
|
|
265
|
+
- Pending job count per queue
|
|
266
|
+
- Active workers per queue
|
|
267
|
+
- Throughput (jobs/minute)
|
|
268
|
+
- Latency (average wait time)
|
|
269
|
+
|
|
270
|
+
**Use Cases:**
|
|
271
|
+
- Identify bottlenecked queues
|
|
272
|
+
- Verify queue priority configuration
|
|
273
|
+
- Monitor worker capacity
|
|
274
|
+
|
|
275
|
+
### Failed Jobs Tab
|
|
276
|
+
|
|
277
|
+
**Features:**
|
|
278
|
+
- Full error backtraces
|
|
279
|
+
- Job arguments and context
|
|
280
|
+
- Retry history and attempt counts
|
|
281
|
+
- Bulk retry/discard operations
|
|
282
|
+
|
|
283
|
+
**Workflows:**
|
|
284
|
+
|
|
285
|
+
1. **Investigating Failures:**
|
|
286
|
+
- Click failed job to see full backtrace
|
|
287
|
+
- Review job arguments for invalid data
|
|
288
|
+
- Check retry history for transient vs persistent failures
|
|
289
|
+
|
|
290
|
+
2. **Bulk Recovery:**
|
|
291
|
+
- Select multiple failed jobs
|
|
292
|
+
- Click "Retry Selected" to requeue
|
|
293
|
+
- Or "Discard Selected" for jobs that can't be fixed
|
|
294
|
+
|
|
295
|
+
3. **Pattern Detection:**
|
|
296
|
+
- Group by error type to find systemic issues
|
|
297
|
+
- Filter by time range to correlate with deployments
|
|
298
|
+
- Search by class name to find job-specific problems
|
|
299
|
+
|
|
300
|
+
### Individual Job Details
|
|
301
|
+
|
|
302
|
+
**Information Displayed:**
|
|
303
|
+
- Job class and queue name
|
|
304
|
+
- Enqueued/started/finished timestamps
|
|
305
|
+
- Duration and execution time
|
|
306
|
+
- Full arguments (with sensitive params filtered)
|
|
307
|
+
- Error message and backtrace (if failed)
|
|
308
|
+
- Retry count and next retry time
|
|
309
|
+
|
|
310
|
+
**Available Actions:**
|
|
311
|
+
- Retry job (failed jobs only)
|
|
312
|
+
- Discard job (remove from queue)
|
|
313
|
+
- View full execution context
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Configuration Options
|
|
318
|
+
|
|
319
|
+
### Job Retention
|
|
320
|
+
|
|
321
|
+
Control how long finished and failed jobs are kept in the database:
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
# config/initializers/mission_control.rb
|
|
325
|
+
MissionControl::Jobs.configure do |config|
|
|
326
|
+
# Keep finished jobs for 2 weeks (default: 7 days)
|
|
327
|
+
config.finished_jobs_retention_period = 14.days
|
|
328
|
+
|
|
329
|
+
# Keep failed jobs for 3 months (default: 30 days)
|
|
330
|
+
config.failed_jobs_retention_period = 90.days
|
|
331
|
+
end
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Automatic Cleanup:**
|
|
335
|
+
|
|
336
|
+
SolidQueue automatically cleans up old jobs based on these settings. No manual intervention needed.
|
|
337
|
+
|
|
338
|
+
**Manual Cleanup:**
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
# Rails console
|
|
342
|
+
SolidQueue::Job.finished.where("finished_at < ?", 14.days.ago).delete_all
|
|
343
|
+
SolidQueue::Job.failed.where("failed_at < ?", 90.days.ago).delete_all
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Parameter Filtering
|
|
347
|
+
|
|
348
|
+
Prevent sensitive data from appearing in the dashboard:
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
MissionControl::Jobs.configure do |config|
|
|
352
|
+
# Filter these parameter keys from display
|
|
353
|
+
config.filter_parameters = [
|
|
354
|
+
:password,
|
|
355
|
+
:token,
|
|
356
|
+
:secret,
|
|
357
|
+
:api_key,
|
|
358
|
+
:private_key,
|
|
359
|
+
:access_token,
|
|
360
|
+
:refresh_token,
|
|
361
|
+
:credit_card,
|
|
362
|
+
:ssn
|
|
363
|
+
]
|
|
364
|
+
end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Example Job Arguments:**
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
# Job enqueued with:
|
|
371
|
+
SendEmailJob.perform_later(
|
|
372
|
+
user_id: 123,
|
|
373
|
+
password: "secret123",
|
|
374
|
+
api_token: "sk_live_abc123"
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Displayed in Mission Control as:
|
|
378
|
+
{
|
|
379
|
+
user_id: 123,
|
|
380
|
+
password: "[FILTERED]",
|
|
381
|
+
api_token: "[FILTERED]"
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Custom Routes
|
|
386
|
+
|
|
387
|
+
Mount at a different path:
|
|
388
|
+
|
|
389
|
+
```ruby
|
|
390
|
+
# config/routes.rb
|
|
391
|
+
mount MissionControl::Jobs::Engine, at: "/admin/background-jobs"
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Access at: `https://yourapp.com/admin/background-jobs`
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Production Deployment Checklist
|
|
399
|
+
|
|
400
|
+
- [ ] `mission_control-jobs` gem added to Gemfile
|
|
401
|
+
- [ ] Bundle installed and Gemfile.lock committed
|
|
402
|
+
- [ ] Routes configured with authentication
|
|
403
|
+
- [ ] Authentication tested in staging environment
|
|
404
|
+
- [ ] Admin users granted access
|
|
405
|
+
- [ ] Parameter filtering configured for sensitive data
|
|
406
|
+
- [ ] Job retention periods configured
|
|
407
|
+
- [ ] Team members notified of dashboard URL
|
|
408
|
+
- [ ] Dashboard access verified in production
|
|
409
|
+
- [ ] Monitoring alerts configured (optional)
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Monitoring & Alerting Integration
|
|
414
|
+
|
|
415
|
+
### Health Check Endpoint
|
|
416
|
+
|
|
417
|
+
Expose job queue health for external monitoring:
|
|
418
|
+
|
|
419
|
+
```ruby
|
|
420
|
+
# app/controllers/health_controller.rb
|
|
421
|
+
class HealthController < ApplicationController
|
|
422
|
+
skip_before_action :authenticate_user! # Public endpoint
|
|
423
|
+
|
|
424
|
+
def jobs
|
|
425
|
+
pending_count = SolidQueue::Job.pending.count
|
|
426
|
+
failed_count = SolidQueue::Job.failed.count
|
|
427
|
+
oldest_pending = oldest_pending_job_age
|
|
428
|
+
|
|
429
|
+
status = if oldest_pending > 30 || failed_count > 100
|
|
430
|
+
:service_unavailable
|
|
431
|
+
else
|
|
432
|
+
:ok
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
render json: {
|
|
436
|
+
status: status == :ok ? "healthy" : "degraded",
|
|
437
|
+
pending_jobs: pending_count,
|
|
438
|
+
failed_jobs: failed_count,
|
|
439
|
+
oldest_pending_minutes: oldest_pending
|
|
440
|
+
}, status: status
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
private
|
|
444
|
+
|
|
445
|
+
def oldest_pending_job_age
|
|
446
|
+
oldest = SolidQueue::Job.pending.order(:created_at).first
|
|
447
|
+
return 0 unless oldest
|
|
448
|
+
((Time.current - oldest.created_at) / 60).round
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# config/routes.rb
|
|
453
|
+
get '/health/jobs', to: 'health#jobs'
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### External Monitoring Setup
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# Uptime monitoring (Pingdom, UptimeRobot, etc.)
|
|
460
|
+
GET https://yourapp.com/health/jobs
|
|
461
|
+
|
|
462
|
+
# Expected response (healthy):
|
|
463
|
+
{
|
|
464
|
+
"status": "healthy",
|
|
465
|
+
"pending_jobs": 42,
|
|
466
|
+
"failed_jobs": 3,
|
|
467
|
+
"oldest_pending_minutes": 2
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
# Alert on:
|
|
471
|
+
# - status != "healthy"
|
|
472
|
+
# - failed_jobs > threshold
|
|
473
|
+
# - oldest_pending_minutes > 30
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Common Operations
|
|
479
|
+
|
|
480
|
+
### Retry All Failed Jobs
|
|
481
|
+
|
|
482
|
+
```ruby
|
|
483
|
+
# Rails console
|
|
484
|
+
SolidQueue::Job.failed.find_each(&:retry!)
|
|
485
|
+
|
|
486
|
+
# Or with Mission Control UI:
|
|
487
|
+
# 1. Navigate to Failed Jobs tab
|
|
488
|
+
# 2. Select all jobs
|
|
489
|
+
# 3. Click "Retry Selected"
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Discard Specific Failed Jobs
|
|
493
|
+
|
|
494
|
+
```ruby
|
|
495
|
+
# Rails console - discard jobs older than 1 week
|
|
496
|
+
SolidQueue::Job.failed
|
|
497
|
+
.where("failed_at < ?", 1.week.ago)
|
|
498
|
+
.delete_all
|
|
499
|
+
|
|
500
|
+
# Or by job class
|
|
501
|
+
SolidQueue::Job.failed
|
|
502
|
+
.where(class_name: "ProblematicJob")
|
|
503
|
+
.delete_all
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Pause/Resume Queue Processing
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
# Not directly supported by SolidQueue
|
|
510
|
+
# Instead, scale workers to 0 in queue.yml and restart
|
|
511
|
+
|
|
512
|
+
# config/queue.yml (temporary)
|
|
513
|
+
production:
|
|
514
|
+
workers:
|
|
515
|
+
- queues: [critical, mailers]
|
|
516
|
+
threads: 5
|
|
517
|
+
processes: 0 # Paused
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Monitor Specific Queue
|
|
521
|
+
|
|
522
|
+
```ruby
|
|
523
|
+
# Rails console
|
|
524
|
+
SolidQueue::Job.where(queue_name: "mailers").pending.count
|
|
525
|
+
SolidQueue::Job.where(queue_name: "mailers").failed.count
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Troubleshooting
|
|
531
|
+
|
|
532
|
+
### Dashboard Not Loading
|
|
533
|
+
|
|
534
|
+
**Symptom:** 404 or routing error
|
|
535
|
+
|
|
536
|
+
**Solutions:**
|
|
537
|
+
1. Verify gem is installed: `bundle list | grep mission_control`
|
|
538
|
+
2. Check routes: `rails routes | grep jobs`
|
|
539
|
+
3. Restart server after adding gem
|
|
540
|
+
4. Check authentication constraints aren't blocking access
|
|
541
|
+
|
|
542
|
+
### Authentication Loop/Redirect
|
|
543
|
+
|
|
544
|
+
**Symptom:** Redirected to login repeatedly
|
|
545
|
+
|
|
546
|
+
**Solutions:**
|
|
547
|
+
1. Verify user is logged in: `current_user` in console
|
|
548
|
+
2. Check authentication lambda: `user.admin?` returns true
|
|
549
|
+
3. Verify Devise configuration allows access to mounted engines
|
|
550
|
+
4. Check for conflicting before_action filters
|
|
551
|
+
|
|
552
|
+
### Slow Dashboard Performance
|
|
553
|
+
|
|
554
|
+
**Symptom:** Dashboard takes >5s to load
|
|
555
|
+
|
|
556
|
+
**Solutions:**
|
|
557
|
+
1. Clean up old finished jobs:
|
|
558
|
+
```ruby
|
|
559
|
+
SolidQueue::Job.finished.where("finished_at < ?", 7.days.ago).delete_all
|
|
560
|
+
```
|
|
561
|
+
2. Add database indexes (if not present):
|
|
562
|
+
```ruby
|
|
563
|
+
add_index :solid_queue_jobs, [:queue_name, :status]
|
|
564
|
+
add_index :solid_queue_jobs, [:status, :created_at]
|
|
565
|
+
```
|
|
566
|
+
3. Reduce retention periods in initializer
|
|
567
|
+
|
|
568
|
+
### Jobs Not Appearing
|
|
569
|
+
|
|
570
|
+
**Symptom:** Dashboard shows 0 jobs but jobs are running
|
|
571
|
+
|
|
572
|
+
**Solutions:**
|
|
573
|
+
1. Verify SolidQueue is configured: `Rails.configuration.active_job.queue_adapter`
|
|
574
|
+
2. Check queue database connection in `config/database.yml`
|
|
575
|
+
3. Run queue migrations: `rails db:migrate:queue`
|
|
576
|
+
4. Verify jobs are using SolidQueue, not inline adapter
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## Security Considerations
|
|
581
|
+
|
|
582
|
+
### Production Hardening
|
|
583
|
+
|
|
584
|
+
1. **Always require authentication:**
|
|
585
|
+
```ruby
|
|
586
|
+
# ❌ NEVER do this in production
|
|
587
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
588
|
+
|
|
589
|
+
# ✅ Always authenticate
|
|
590
|
+
authenticate :user, ->(user) { user.admin? } do
|
|
591
|
+
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
592
|
+
end
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
2. **Filter sensitive parameters:**
|
|
596
|
+
```ruby
|
|
597
|
+
config.filter_parameters = [:password, :token, :secret, :api_key]
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
3. **Use HTTPS only:**
|
|
601
|
+
```ruby
|
|
602
|
+
# config/environments/production.rb
|
|
603
|
+
config.force_ssl = true
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
4. **Limit admin access:**
|
|
607
|
+
- Grant admin rights only to operations team
|
|
608
|
+
- Audit admin user list regularly
|
|
609
|
+
- Use role-based access for granular control
|
|
610
|
+
|
|
611
|
+
5. **Monitor access logs:**
|
|
612
|
+
```ruby
|
|
613
|
+
# Track who accesses Mission Control
|
|
614
|
+
class ApplicationController < ActionController::Base
|
|
615
|
+
before_action :log_mission_control_access, if: :mission_control_request?
|
|
616
|
+
|
|
617
|
+
private
|
|
618
|
+
|
|
619
|
+
def mission_control_request?
|
|
620
|
+
request.path.start_with?('/jobs')
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def log_mission_control_access
|
|
624
|
+
Rails.logger.info(
|
|
625
|
+
"Mission Control accessed by #{current_user&.email} " \
|
|
626
|
+
"from #{request.remote_ip}"
|
|
627
|
+
)
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Additional Resources
|
|
635
|
+
|
|
636
|
+
- [Mission Control Jobs GitHub](https://github.com/rails/mission_control-jobs)
|
|
637
|
+
- [SolidQueue Documentation](https://github.com/rails/solid_queue)
|
|
638
|
+
- [Rails Active Job Guide](https://guides.rubyonrails.org/active_job_basics.html)
|
|
639
|
+
- [Rails 8 Release Notes - Solid Stack](https://edgeguides.rubyonrails.org/8_0_release_notes.html)
|