source_monitor 0.13.1 → 0.15.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/.claude/skills/sm-configuration-setting/reference/settings-catalog.md +1 -0
- data/.claude/skills/sm-configure/SKILL.md +8 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +11 -0
- data/.claude/skills/sm-host-setup/SKILL.md +13 -3
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +11 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +9 -1
- data/.claude/skills/sm-upgrade/reference/version-history.md +26 -0
- data/AGENTS.md +145 -38
- data/CHANGELOG.md +29 -0
- data/CLAUDE.md +6 -116
- data/Gemfile.lock +86 -86
- data/README.md +3 -3
- data/VERSION +1 -1
- data/app/controllers/source_monitor/application_controller.rb +73 -14
- data/app/views/layouts/source_monitor/application.html.erb +6 -0
- data/docs/configuration.md +18 -1
- data/docs/deployment.md +1 -1
- data/docs/goals/engine-hardening/.goalbuddy-board/app.js +543 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/goalbuddy-mark.png +0 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/index.html +111 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/styles.css +991 -0
- data/docs/goals/engine-hardening/goal.md +97 -0
- data/docs/goals/engine-hardening/notes/T001-spec-validation.md +37 -0
- data/docs/goals/engine-hardening/state.yaml +324 -0
- data/docs/setup.md +4 -4
- data/docs/upgrade.md +41 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +10 -0
- data/lib/source_monitor/configuration/authentication_settings.rb +5 -1
- data/lib/source_monitor/security/authentication.rb +10 -0
- data/lib/source_monitor/version.rb +1 -1
- data/source_monitor.gemspec +8 -3
- metadata +10 -67
- data/.claude/agent-memory/vbw-vbw-debugger/MEMORY.md +0 -15
- data/.claude/agent-memory/vbw-vbw-dev/MEMORY.md +0 -34
- data/.claude/agent-memory/vbw-vbw-lead/MEMORY.md +0 -49
- data/.claude/agents/rails-concern.md +0 -464
- data/.claude/agents/rails-controller.md +0 -424
- data/.claude/agents/rails-hotwire.md +0 -446
- data/.claude/agents/rails-implement.md +0 -374
- data/.claude/agents/rails-job.md +0 -334
- data/.claude/agents/rails-lint.md +0 -294
- data/.claude/agents/rails-mailer.md +0 -371
- data/.claude/agents/rails-migration.md +0 -449
- data/.claude/agents/rails-model.md +0 -420
- data/.claude/agents/rails-policy.md +0 -443
- data/.claude/agents/rails-presenter.md +0 -427
- data/.claude/agents/rails-query.md +0 -412
- data/.claude/agents/rails-review.md +0 -490
- data/.claude/agents/rails-service.md +0 -458
- data/.claude/agents/rails-state-records.md +0 -465
- data/.claude/agents/rails-tdd.md +0 -314
- data/.claude/agents/rails-test.md +0 -441
- data/.claude/agents/rails-view-component.md +0 -418
- data/.claude/commands/rails-audit.md +0 -77
- data/.claude/commands/release.md +0 -366
- data/.claude/hooks/block-secrets.sh +0 -52
- data/.claude/settings.json +0 -85
- data/.claude/skills/action-cable-patterns/SKILL.md +0 -296
- data/.claude/skills/action-mailer-patterns/SKILL.md +0 -295
- data/.claude/skills/active-storage-setup/SKILL.md +0 -311
- data/.claude/skills/api-versioning/SKILL.md +0 -294
- data/.claude/skills/authentication-flow/SKILL.md +0 -335
- data/.claude/skills/authentication-flow/reference/current.md +0 -248
- data/.claude/skills/authentication-flow/reference/passwordless.md +0 -253
- data/.claude/skills/authentication-flow/reference/sessions.md +0 -201
- data/.claude/skills/authorization-pundit/SKILL.md +0 -462
- data/.claude/skills/caching-strategies/SKILL.md +0 -350
- data/.claude/skills/database-migrations/SKILL.md +0 -354
- data/.claude/skills/form-object-patterns/SKILL.md +0 -399
- data/.claude/skills/hotwire-patterns/SKILL.md +0 -247
- data/.claude/skills/hotwire-patterns/reference/stimulus.md +0 -307
- data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +0 -112
- data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +0 -158
- data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +0 -218
- data/.claude/skills/i18n-patterns/SKILL.md +0 -320
- data/.claude/skills/install/SKILL.md +0 -367
- data/.claude/skills/performance-optimization/SKILL.md +0 -311
- data/.claude/skills/rails-architecture/SKILL.md +0 -259
- data/.claude/skills/rails-architecture/reference/error-handling.md +0 -333
- data/.claude/skills/rails-architecture/reference/event-tracking.md +0 -142
- data/.claude/skills/rails-architecture/reference/layer-interactions.md +0 -417
- data/.claude/skills/rails-architecture/reference/multi-tenancy.md +0 -152
- data/.claude/skills/rails-architecture/reference/query-patterns.md +0 -342
- data/.claude/skills/rails-architecture/reference/service-patterns.md +0 -286
- data/.claude/skills/rails-architecture/reference/state-records.md +0 -250
- data/.claude/skills/rails-architecture/reference/testing-strategy.md +0 -326
- data/.claude/skills/rails-concern/SKILL.md +0 -399
- data/.claude/skills/rails-controller/SKILL.md +0 -336
- data/.claude/skills/rails-model-generator/SKILL.md +0 -321
- data/.claude/skills/rails-model-generator/reference/validations.md +0 -298
- data/.claude/skills/rails-presenter/SKILL.md +0 -274
- data/.claude/skills/rails-query-object/SKILL.md +0 -289
- data/.claude/skills/rails-service-object/SKILL.md +0 -349
- data/.claude/skills/solid-queue-setup/SKILL.md +0 -307
- data/.claude/skills/tdd-cycle/SKILL.md +0 -359
- data/.claude/skills/viewcomponent-patterns/SKILL.md +0 -333
data/CLAUDE.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Core value:** Drop-in Rails engine for feed monitoring, content scraping, and operational dashboards.
|
|
4
4
|
|
|
5
|
+
> **Engine conventions live in [`AGENTS.md`](AGENTS.md)** — the canonical, cross-agent reference for tech stack, architecture, testing, quality gates, the pre-push CI checklist, security rules, configuration DSL, and commands. This file holds only Claude Code-specific context: working memory, VBW commands, and the `.claude/` agent/skill catalogs. When a convention changes, edit `AGENTS.md`, not here.
|
|
6
|
+
|
|
5
7
|
## Active Context
|
|
6
8
|
|
|
7
9
|
**Last shipped:** rails-audit-and-refactoring (7 phases, 30 plans)
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
## Key Decisions
|
|
11
13
|
|
|
12
14
|
- Keep PostgreSQL-only for now
|
|
13
|
-
- Keep host-app auth model
|
|
15
|
+
- Keep host-app auth model (fail-closed by default since #129)
|
|
14
16
|
- Ruby autoload for lib/ modules (not Zeitwerk)
|
|
15
17
|
- PG parallel fork segfault resolved: switched to thread-based parallelism in aia-ssl-fix milestone
|
|
16
18
|
|
|
@@ -23,13 +25,6 @@
|
|
|
23
25
|
- vastai (global)
|
|
24
26
|
- find-skills (global)
|
|
25
27
|
|
|
26
|
-
## Learned Patterns
|
|
27
|
-
|
|
28
|
-
- Sub-module extraction: create `module/submodule.rb` with `require_relative`, lazy accessors, forwarding methods for backward compat
|
|
29
|
-
- Coverage runs need `COVERAGE=1 PARALLEL_WORKERS=1` with threads (not forks) to avoid PG segfault and SimpleCov data loss
|
|
30
|
-
- Test isolation: scope queries to specific source/item to prevent cross-test contamination in parallel runs
|
|
31
|
-
- RuboCop omakase: only 45/775 cops enabled, all Metrics cops disabled -- no file size enforcement
|
|
32
|
-
|
|
33
28
|
## VBW Commands
|
|
34
29
|
|
|
35
30
|
This project uses VBW (Vibe Better with Claude Code).
|
|
@@ -38,111 +33,10 @@ Run /vbw:help for all commands.
|
|
|
38
33
|
|
|
39
34
|
---
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
## Tech Stack
|
|
44
|
-
|
|
45
|
-
| Layer | Technology |
|
|
46
|
-
|-------|------------|
|
|
47
|
-
| Ruby | 4.0+ |
|
|
48
|
-
| Rails | 8.x |
|
|
49
|
-
| Testing | Minitest (no fixtures -- uses factory helpers + WebMock/VCR) |
|
|
50
|
-
| Authorization | Host app responsibility (mountable engine) |
|
|
51
|
-
| Jobs | Solid Queue |
|
|
52
|
-
| Frontend | Hotwire (Turbo + Stimulus) + Tailwind CSS |
|
|
53
|
-
| Linting | RuboCop (omakase) + Brakeman |
|
|
54
|
-
| Database | PostgreSQL only |
|
|
55
|
-
|
|
56
|
-
## Architecture Conventions
|
|
57
|
-
|
|
58
|
-
### Models First
|
|
59
|
-
- Business logic lives in models. Use concerns for horizontal sharing.
|
|
60
|
-
- Service objects ONLY for operations spanning 3+ models or external integrations.
|
|
61
|
-
- Query objects for complex queries that don't fit a single scope.
|
|
62
|
-
- Presenters (SimpleDelegator) for view-specific formatting.
|
|
63
|
-
|
|
64
|
-
### Everything-is-CRUD Routing
|
|
65
|
-
- Prefer creating a new resource over adding custom actions.
|
|
66
|
-
- `POST /posts/:id/publications` over `POST /posts/:id/publish`.
|
|
67
|
-
- RESTful routes only; no `member` or `collection` blocks with custom verbs.
|
|
68
|
-
|
|
69
|
-
### State as Records
|
|
70
|
-
- Track business state transitions as separate records (who/when/why).
|
|
71
|
-
- Boolean columns ONLY for technical flags (e.g., `email_verified`).
|
|
72
|
-
|
|
73
|
-
### Jobs
|
|
74
|
-
- Shallow jobs: call `_later` or `_now` methods on models/services.
|
|
75
|
-
- Jobs contain only deserialization + delegation. No business logic.
|
|
76
|
-
- Use Solid Queue recurring jobs for scheduled work.
|
|
77
|
-
|
|
78
|
-
### Frontend
|
|
79
|
-
- Turbo Frames for partial page updates.
|
|
80
|
-
- Turbo Streams for real-time broadcasts.
|
|
81
|
-
- Stimulus controllers: small, focused, one behavior each.
|
|
82
|
-
- Tailwind CSS utility classes; extract components for repeated patterns.
|
|
83
|
-
|
|
84
|
-
## Testing Conventions
|
|
85
|
-
|
|
86
|
-
- **Framework:** Minitest. NEVER use RSpec or FactoryBot.
|
|
87
|
-
- **Helpers:** `create_source!` factory, `with_inline_jobs`, `with_queue_adapter`.
|
|
88
|
-
- **HTTP:** WebMock disables external HTTP; VCR for recorded cassettes.
|
|
89
|
-
- **Config:** Reset every test with `SourceMonitor.reset_configuration!`.
|
|
90
|
-
- **TDD workflow:** Red (failing test) -> Green (minimal pass) -> Refactor.
|
|
91
|
-
- **Coverage:** Every model validation, scope, and public method. Every controller action.
|
|
92
|
-
|
|
93
|
-
## Quality Gates
|
|
94
|
-
|
|
95
|
-
- `bin/rubocop` -- zero offenses before commit.
|
|
96
|
-
- `bin/brakeman --no-pager` -- zero warnings before merge.
|
|
97
|
-
- `bin/rails test` -- all tests pass.
|
|
98
|
-
- `yarn build` -- rebuild JS assets if any `.js` files changed (ESLint runs in CI).
|
|
99
|
-
- No N+1 queries (use `includes`/`preload`).
|
|
100
|
-
- No hardcoded credentials (use Rails credentials or ENV).
|
|
101
|
-
|
|
102
|
-
### Pre-Push CI Checklist (run ALL before pushing to GitHub)
|
|
103
|
-
|
|
104
|
-
Before pushing any branch (especially release branches), run the full CI equivalent locally:
|
|
105
|
-
|
|
106
|
-
1. `bin/rubocop` -- catches Ruby lint issues
|
|
107
|
-
2. `PARALLEL_WORKERS=1 bin/rails test` -- catches test failures AND diff coverage gaps
|
|
108
|
-
3. `bin/brakeman --no-pager` -- catches security issues
|
|
109
|
-
4. `yarn build` -- rebuilds JS and catches ESLint issues (CI runs ESLint separately)
|
|
110
|
-
|
|
111
|
-
**Why:** CI failures cost ~5 min per round-trip. In v0.8.0, skipping ESLint and diff coverage checks locally caused 2 wasted CI cycles. Common blind spots:
|
|
112
|
-
- JS files need `/* global */` declarations for browser APIs (MutationObserver, requestAnimationFrame, etc.)
|
|
113
|
-
- Every `rescue` / fallback / error path in new source code needs test coverage (CI diff coverage gate rejects uncovered lines)
|
|
114
|
-
- `yarn build` must run after JS changes to sync sourcemaps
|
|
115
|
-
|
|
116
|
-
## QA and UAT Rules
|
|
36
|
+
## QA and UAT Rules (Claude Code)
|
|
117
37
|
|
|
118
38
|
- **Browser-first verification:** During VBW QA (`/vbw:qa`) and UAT (`/vbw:verify`), ALWAYS start by using `agent-browser` to test UI scenarios yourself before presenting checkpoints to the user. Navigate to the dummy app (port 3002), take snapshots/screenshots, and verify visual and functional behavior with agents first.
|
|
119
|
-
- **Automate what you can:**
|
|
120
|
-
- **Dummy app port:** The SourceMonitor dummy app runs on port 3002 (`cd test/dummy && bin/rails server -p 3002`).
|
|
121
|
-
|
|
122
|
-
## Security Rules
|
|
123
|
-
|
|
124
|
-
### Protected Files (NEVER read or output)
|
|
125
|
-
- `.env`, `.env.*`
|
|
126
|
-
- `config/master.key`, `config/credentials.yml.enc`
|
|
127
|
-
- `.kamal/secrets`
|
|
128
|
-
- Any `*.pem`, `*.key` files
|
|
129
|
-
|
|
130
|
-
### Forbidden Operations
|
|
131
|
-
- `git push --force` to main/master/production
|
|
132
|
-
- `git reset --hard` without explicit user confirmation
|
|
133
|
-
- `rm -rf` on root, home, or parent directories
|
|
134
|
-
- `chmod 777`
|
|
135
|
-
|
|
136
|
-
## Development Commands
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
bin/dev # Start dev server
|
|
140
|
-
bin/rails test # Run all tests
|
|
141
|
-
bin/rubocop # Check style
|
|
142
|
-
bin/rubocop -a # Auto-fix style
|
|
143
|
-
bin/brakeman --no-pager # Security scan
|
|
144
|
-
bin/rails db:migrate # Run migrations
|
|
145
|
-
```
|
|
39
|
+
- **Automate what you can:** anything verifiable programmatically should be a test — see the Testing section in [`AGENTS.md`](AGENTS.md) — and only present truly visual/interactive checks to the user.
|
|
146
40
|
|
|
147
41
|
## Agent Catalog
|
|
148
42
|
|
|
@@ -200,7 +94,7 @@ These skills are available in `.claude/skills/`:
|
|
|
200
94
|
|
|
201
95
|
## Source Monitor Skills
|
|
202
96
|
|
|
203
|
-
Engine-specific skills (`sm-*` prefix). Consumer skills install by default; contributor skills are opt-in.
|
|
97
|
+
Engine-specific skills (`sm-*` prefix). Consumer skills install by default; contributor skills are opt-in. The **Skills & Docs Alignment** maintenance rule lives in [`AGENTS.md`](AGENTS.md).
|
|
204
98
|
|
|
205
99
|
### Consumer Skills (default install)
|
|
206
100
|
|
|
@@ -237,7 +131,3 @@ bin/rails source_monitor:skills:contributor # Contributor skills
|
|
|
237
131
|
bin/rails source_monitor:skills:all # All skills
|
|
238
132
|
bin/rails source_monitor:skills:remove # Remove all sm-* skills
|
|
239
133
|
```
|
|
240
|
-
|
|
241
|
-
## Maintenance Rules
|
|
242
|
-
|
|
243
|
-
- **Skills & docs alignment**: Whenever engine code changes (models, configuration, pipeline, jobs, migrations, scrapers, events, health rules, or dashboard), the corresponding `sm-*` skill and its `reference/` files MUST be updated in the same PR to ensure skills always reflect current engine behavior.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
source_monitor (0.
|
|
4
|
+
source_monitor (0.15.0)
|
|
5
5
|
cssbundling-rails (~> 1.4)
|
|
6
6
|
faraday (~> 2.9)
|
|
7
7
|
faraday-follow_redirects (~> 0.4)
|
|
@@ -16,36 +16,36 @@ PATH
|
|
|
16
16
|
solid_cable (>= 3.0, < 4.0)
|
|
17
17
|
solid_queue (>= 0.3, < 3.0)
|
|
18
18
|
turbo-rails (~> 2.0)
|
|
19
|
-
view_component (>= 3.0, <
|
|
19
|
+
view_component (>= 3.0, < 5.0)
|
|
20
20
|
|
|
21
21
|
GEM
|
|
22
22
|
remote: https://rubygems.org/
|
|
23
23
|
specs:
|
|
24
|
-
action_text-trix (2.1.
|
|
24
|
+
action_text-trix (2.1.17)
|
|
25
25
|
railties
|
|
26
|
-
actioncable (8.1.
|
|
27
|
-
actionpack (= 8.1.
|
|
28
|
-
activesupport (= 8.1.
|
|
26
|
+
actioncable (8.1.3)
|
|
27
|
+
actionpack (= 8.1.3)
|
|
28
|
+
activesupport (= 8.1.3)
|
|
29
29
|
nio4r (~> 2.0)
|
|
30
30
|
websocket-driver (>= 0.6.1)
|
|
31
31
|
zeitwerk (~> 2.6)
|
|
32
|
-
actionmailbox (8.1.
|
|
33
|
-
actionpack (= 8.1.
|
|
34
|
-
activejob (= 8.1.
|
|
35
|
-
activerecord (= 8.1.
|
|
36
|
-
activestorage (= 8.1.
|
|
37
|
-
activesupport (= 8.1.
|
|
32
|
+
actionmailbox (8.1.3)
|
|
33
|
+
actionpack (= 8.1.3)
|
|
34
|
+
activejob (= 8.1.3)
|
|
35
|
+
activerecord (= 8.1.3)
|
|
36
|
+
activestorage (= 8.1.3)
|
|
37
|
+
activesupport (= 8.1.3)
|
|
38
38
|
mail (>= 2.8.0)
|
|
39
|
-
actionmailer (8.1.
|
|
40
|
-
actionpack (= 8.1.
|
|
41
|
-
actionview (= 8.1.
|
|
42
|
-
activejob (= 8.1.
|
|
43
|
-
activesupport (= 8.1.
|
|
39
|
+
actionmailer (8.1.3)
|
|
40
|
+
actionpack (= 8.1.3)
|
|
41
|
+
actionview (= 8.1.3)
|
|
42
|
+
activejob (= 8.1.3)
|
|
43
|
+
activesupport (= 8.1.3)
|
|
44
44
|
mail (>= 2.8.0)
|
|
45
45
|
rails-dom-testing (~> 2.2)
|
|
46
|
-
actionpack (8.1.
|
|
47
|
-
actionview (= 8.1.
|
|
48
|
-
activesupport (= 8.1.
|
|
46
|
+
actionpack (8.1.3)
|
|
47
|
+
actionview (= 8.1.3)
|
|
48
|
+
activesupport (= 8.1.3)
|
|
49
49
|
nokogiri (>= 1.8.5)
|
|
50
50
|
rack (>= 2.2.4)
|
|
51
51
|
rack-session (>= 1.0.1)
|
|
@@ -53,36 +53,36 @@ GEM
|
|
|
53
53
|
rails-dom-testing (~> 2.2)
|
|
54
54
|
rails-html-sanitizer (~> 1.6)
|
|
55
55
|
useragent (~> 0.16)
|
|
56
|
-
actiontext (8.1.
|
|
56
|
+
actiontext (8.1.3)
|
|
57
57
|
action_text-trix (~> 2.1.15)
|
|
58
|
-
actionpack (= 8.1.
|
|
59
|
-
activerecord (= 8.1.
|
|
60
|
-
activestorage (= 8.1.
|
|
61
|
-
activesupport (= 8.1.
|
|
58
|
+
actionpack (= 8.1.3)
|
|
59
|
+
activerecord (= 8.1.3)
|
|
60
|
+
activestorage (= 8.1.3)
|
|
61
|
+
activesupport (= 8.1.3)
|
|
62
62
|
globalid (>= 0.6.0)
|
|
63
63
|
nokogiri (>= 1.8.5)
|
|
64
|
-
actionview (8.1.
|
|
65
|
-
activesupport (= 8.1.
|
|
64
|
+
actionview (8.1.3)
|
|
65
|
+
activesupport (= 8.1.3)
|
|
66
66
|
builder (~> 3.1)
|
|
67
67
|
erubi (~> 1.11)
|
|
68
68
|
rails-dom-testing (~> 2.2)
|
|
69
69
|
rails-html-sanitizer (~> 1.6)
|
|
70
|
-
activejob (8.1.
|
|
71
|
-
activesupport (= 8.1.
|
|
70
|
+
activejob (8.1.3)
|
|
71
|
+
activesupport (= 8.1.3)
|
|
72
72
|
globalid (>= 0.3.6)
|
|
73
|
-
activemodel (8.1.
|
|
74
|
-
activesupport (= 8.1.
|
|
75
|
-
activerecord (8.1.
|
|
76
|
-
activemodel (= 8.1.
|
|
77
|
-
activesupport (= 8.1.
|
|
73
|
+
activemodel (8.1.3)
|
|
74
|
+
activesupport (= 8.1.3)
|
|
75
|
+
activerecord (8.1.3)
|
|
76
|
+
activemodel (= 8.1.3)
|
|
77
|
+
activesupport (= 8.1.3)
|
|
78
78
|
timeout (>= 0.4.0)
|
|
79
|
-
activestorage (8.1.
|
|
80
|
-
actionpack (= 8.1.
|
|
81
|
-
activejob (= 8.1.
|
|
82
|
-
activerecord (= 8.1.
|
|
83
|
-
activesupport (= 8.1.
|
|
79
|
+
activestorage (8.1.3)
|
|
80
|
+
actionpack (= 8.1.3)
|
|
81
|
+
activejob (= 8.1.3)
|
|
82
|
+
activerecord (= 8.1.3)
|
|
83
|
+
activesupport (= 8.1.3)
|
|
84
84
|
marcel (~> 1.0)
|
|
85
|
-
activesupport (8.1.
|
|
85
|
+
activesupport (8.1.3)
|
|
86
86
|
base64
|
|
87
87
|
bigdecimal
|
|
88
88
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -95,12 +95,12 @@ GEM
|
|
|
95
95
|
securerandom (>= 0.3)
|
|
96
96
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
97
97
|
uri (>= 0.13.1)
|
|
98
|
-
addressable (2.
|
|
99
|
-
public_suffix (>= 2.0.2, <
|
|
98
|
+
addressable (2.9.0)
|
|
99
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
100
100
|
ast (2.4.3)
|
|
101
101
|
base64 (0.3.0)
|
|
102
|
-
bigdecimal (4.
|
|
103
|
-
brakeman (8.0.
|
|
102
|
+
bigdecimal (4.1.2)
|
|
103
|
+
brakeman (8.0.4)
|
|
104
104
|
racc
|
|
105
105
|
builder (3.3.0)
|
|
106
106
|
capybara (3.40.0)
|
|
@@ -125,7 +125,7 @@ GEM
|
|
|
125
125
|
digest (3.2.1)
|
|
126
126
|
docile (1.4.1)
|
|
127
127
|
drb (2.2.3)
|
|
128
|
-
erb (6.0.
|
|
128
|
+
erb (6.0.2)
|
|
129
129
|
erubi (1.13.1)
|
|
130
130
|
et-orbi (1.4.0)
|
|
131
131
|
tzinfo
|
|
@@ -163,11 +163,11 @@ GEM
|
|
|
163
163
|
reline (>= 0.4.2)
|
|
164
164
|
jsbundling-rails (1.3.1)
|
|
165
165
|
railties (>= 6.0.0)
|
|
166
|
-
json (2.
|
|
166
|
+
json (2.19.2)
|
|
167
167
|
language_server-protocol (3.17.0.5)
|
|
168
168
|
lint_roller (1.1.0)
|
|
169
169
|
logger (1.7.0)
|
|
170
|
-
loofah (2.25.
|
|
170
|
+
loofah (2.25.1)
|
|
171
171
|
crass (~> 1.0.2)
|
|
172
172
|
nokogiri (>= 1.12.0)
|
|
173
173
|
mail (2.9.0)
|
|
@@ -178,17 +178,17 @@ GEM
|
|
|
178
178
|
net-smtp
|
|
179
179
|
marcel (1.1.0)
|
|
180
180
|
matrix (0.4.3)
|
|
181
|
-
method_source (1.1.0)
|
|
182
181
|
mini_magick (5.3.1)
|
|
183
182
|
logger
|
|
184
183
|
mini_mime (1.1.5)
|
|
185
184
|
mini_portile2 (2.8.9)
|
|
186
|
-
minitest (6.0.
|
|
185
|
+
minitest (6.0.2)
|
|
186
|
+
drb (~> 2.0)
|
|
187
187
|
prism (~> 1.5)
|
|
188
188
|
minitest-mock (5.27.0)
|
|
189
189
|
net-http (0.9.1)
|
|
190
190
|
uri (>= 0.11.1)
|
|
191
|
-
net-imap (0.6.
|
|
191
|
+
net-imap (0.6.3)
|
|
192
192
|
date
|
|
193
193
|
net-protocol
|
|
194
194
|
net-pop (0.1.2)
|
|
@@ -198,18 +198,18 @@ GEM
|
|
|
198
198
|
net-smtp (0.5.1)
|
|
199
199
|
net-protocol
|
|
200
200
|
nio4r (2.7.5)
|
|
201
|
-
nokogiri (1.19.
|
|
201
|
+
nokogiri (1.19.2)
|
|
202
202
|
mini_portile2 (~> 2.8.2)
|
|
203
203
|
racc (~> 1.4)
|
|
204
|
-
nokogiri (1.19.
|
|
204
|
+
nokogiri (1.19.2-aarch64-linux-gnu)
|
|
205
205
|
racc (~> 1.4)
|
|
206
|
-
nokogiri (1.19.
|
|
206
|
+
nokogiri (1.19.2-aarch64-linux-musl)
|
|
207
207
|
racc (~> 1.4)
|
|
208
|
-
nokogiri (1.19.
|
|
208
|
+
nokogiri (1.19.2-arm-linux-gnu)
|
|
209
209
|
racc (~> 1.4)
|
|
210
|
-
nokogiri (1.19.
|
|
210
|
+
nokogiri (1.19.2-arm-linux-musl)
|
|
211
211
|
racc (~> 1.4)
|
|
212
|
-
nokolexbor (0.6.
|
|
212
|
+
nokolexbor (0.6.4)
|
|
213
213
|
ostruct (0.6.3)
|
|
214
214
|
parallel (1.27.0)
|
|
215
215
|
parser (3.3.10.1)
|
|
@@ -229,12 +229,12 @@ GEM
|
|
|
229
229
|
psych (5.3.1)
|
|
230
230
|
date
|
|
231
231
|
stringio
|
|
232
|
-
public_suffix (
|
|
232
|
+
public_suffix (7.0.5)
|
|
233
233
|
puma (7.2.0)
|
|
234
234
|
nio4r (~> 2.0)
|
|
235
235
|
raabro (1.4.0)
|
|
236
236
|
racc (1.8.1)
|
|
237
|
-
rack (3.2.
|
|
237
|
+
rack (3.2.5)
|
|
238
238
|
rack-session (2.1.1)
|
|
239
239
|
base64 (>= 0.1.0)
|
|
240
240
|
rack (>= 3.0.0)
|
|
@@ -242,30 +242,30 @@ GEM
|
|
|
242
242
|
rack (>= 1.3)
|
|
243
243
|
rackup (2.3.1)
|
|
244
244
|
rack (>= 3)
|
|
245
|
-
rails (8.1.
|
|
246
|
-
actioncable (= 8.1.
|
|
247
|
-
actionmailbox (= 8.1.
|
|
248
|
-
actionmailer (= 8.1.
|
|
249
|
-
actionpack (= 8.1.
|
|
250
|
-
actiontext (= 8.1.
|
|
251
|
-
actionview (= 8.1.
|
|
252
|
-
activejob (= 8.1.
|
|
253
|
-
activemodel (= 8.1.
|
|
254
|
-
activerecord (= 8.1.
|
|
255
|
-
activestorage (= 8.1.
|
|
256
|
-
activesupport (= 8.1.
|
|
245
|
+
rails (8.1.3)
|
|
246
|
+
actioncable (= 8.1.3)
|
|
247
|
+
actionmailbox (= 8.1.3)
|
|
248
|
+
actionmailer (= 8.1.3)
|
|
249
|
+
actionpack (= 8.1.3)
|
|
250
|
+
actiontext (= 8.1.3)
|
|
251
|
+
actionview (= 8.1.3)
|
|
252
|
+
activejob (= 8.1.3)
|
|
253
|
+
activemodel (= 8.1.3)
|
|
254
|
+
activerecord (= 8.1.3)
|
|
255
|
+
activestorage (= 8.1.3)
|
|
256
|
+
activesupport (= 8.1.3)
|
|
257
257
|
bundler (>= 1.15.0)
|
|
258
|
-
railties (= 8.1.
|
|
258
|
+
railties (= 8.1.3)
|
|
259
259
|
rails-dom-testing (2.3.0)
|
|
260
260
|
activesupport (>= 5.0.0)
|
|
261
261
|
minitest
|
|
262
262
|
nokogiri (>= 1.6)
|
|
263
|
-
rails-html-sanitizer (1.
|
|
264
|
-
loofah (~> 2.
|
|
263
|
+
rails-html-sanitizer (1.7.0)
|
|
264
|
+
loofah (~> 2.25)
|
|
265
265
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
266
|
-
railties (8.1.
|
|
267
|
-
actionpack (= 8.1.
|
|
268
|
-
activesupport (= 8.1.
|
|
266
|
+
railties (8.1.3)
|
|
267
|
+
actionpack (= 8.1.3)
|
|
268
|
+
activesupport (= 8.1.3)
|
|
269
269
|
irb (~> 1.13)
|
|
270
270
|
rackup (>= 1.0.0)
|
|
271
271
|
rake (>= 12.2)
|
|
@@ -321,7 +321,7 @@ GEM
|
|
|
321
321
|
rubyzip (3.2.2)
|
|
322
322
|
sax-machine (1.3.2)
|
|
323
323
|
securerandom (0.4.1)
|
|
324
|
-
selenium-webdriver (4.
|
|
324
|
+
selenium-webdriver (4.41.0)
|
|
325
325
|
base64 (~> 0.2)
|
|
326
326
|
logger (~> 1.4)
|
|
327
327
|
rexml (~> 3.2, >= 3.2.5)
|
|
@@ -338,18 +338,18 @@ GEM
|
|
|
338
338
|
activejob (>= 7.2)
|
|
339
339
|
activerecord (>= 7.2)
|
|
340
340
|
railties (>= 7.2)
|
|
341
|
-
solid_queue (1.
|
|
341
|
+
solid_queue (1.4.0)
|
|
342
342
|
activejob (>= 7.1)
|
|
343
343
|
activerecord (>= 7.1)
|
|
344
344
|
concurrent-ruby (>= 1.3.1)
|
|
345
345
|
fugit (~> 1.11)
|
|
346
346
|
railties (>= 7.1)
|
|
347
347
|
thor (>= 1.3.1)
|
|
348
|
-
stackprof (0.2.
|
|
348
|
+
stackprof (0.2.28)
|
|
349
349
|
stringio (3.2.0)
|
|
350
|
-
test-prof (1.
|
|
350
|
+
test-prof (1.6.0)
|
|
351
351
|
thor (1.5.0)
|
|
352
|
-
timeout (0.6.
|
|
352
|
+
timeout (0.6.1)
|
|
353
353
|
tsort (0.2.0)
|
|
354
354
|
turbo-rails (2.0.23)
|
|
355
355
|
actionpack (>= 7.1.0)
|
|
@@ -362,11 +362,11 @@ GEM
|
|
|
362
362
|
uri (1.1.1)
|
|
363
363
|
useragent (0.16.11)
|
|
364
364
|
vcr (6.4.0)
|
|
365
|
-
view_component (
|
|
366
|
-
|
|
365
|
+
view_component (4.5.0)
|
|
366
|
+
actionview (>= 7.1.0)
|
|
367
|
+
activesupport (>= 7.1.0)
|
|
367
368
|
concurrent-ruby (~> 1)
|
|
368
|
-
|
|
369
|
-
webmock (3.26.1)
|
|
369
|
+
webmock (3.26.2)
|
|
370
370
|
addressable (>= 2.8.0)
|
|
371
371
|
crack (>= 0.3.2)
|
|
372
372
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -377,7 +377,7 @@ GEM
|
|
|
377
377
|
websocket-extensions (0.1.5)
|
|
378
378
|
xpath (3.2.0)
|
|
379
379
|
nokogiri (~> 1.8)
|
|
380
|
-
zeitwerk (2.7.
|
|
380
|
+
zeitwerk (2.7.5)
|
|
381
381
|
zlib (3.2.2)
|
|
382
382
|
|
|
383
383
|
PLATFORMS
|
data/README.md
CHANGED
|
@@ -9,8 +9,8 @@ SourceMonitor is a production-ready Rails 8 mountable engine for ingesting, norm
|
|
|
9
9
|
In your host Rails app:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
bundle add source_monitor --version "~> 0.
|
|
13
|
-
# or add `gem "source_monitor", "~> 0.
|
|
12
|
+
bundle add source_monitor --version "~> 0.15.0"
|
|
13
|
+
# or add `gem "source_monitor", "~> 0.15.0"` manually, then run:
|
|
14
14
|
bundle install
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -46,7 +46,7 @@ This exposes `bin/source_monitor` (via Bundler binstubs) so you can run the guid
|
|
|
46
46
|
Before running any SourceMonitor commands inside your host app, add the gem and install dependencies:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
bundle add source_monitor --version "~> 0.
|
|
49
|
+
bundle add source_monitor --version "~> 0.15.0"
|
|
50
50
|
# or edit your Gemfile, then run
|
|
51
51
|
bundle install
|
|
52
52
|
```
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.15.0
|
|
@@ -4,11 +4,13 @@ module SourceMonitor
|
|
|
4
4
|
class ApplicationController < ActionController::Base
|
|
5
5
|
protect_from_forgery with: :exception, prepend: true
|
|
6
6
|
|
|
7
|
+
before_action :enforce_source_monitor_access_default
|
|
7
8
|
before_action :authenticate_source_monitor_user
|
|
8
9
|
before_action :authorize_source_monitor_access
|
|
9
10
|
|
|
10
|
-
helper_method :source_monitor_current_user, :source_monitor_user_signed_in
|
|
11
|
-
|
|
11
|
+
helper_method :source_monitor_current_user, :source_monitor_user_signed_in?,
|
|
12
|
+
:source_monitor_flash_toasts
|
|
13
|
+
after_action :append_flash_toasts_to_turbo_stream
|
|
12
14
|
|
|
13
15
|
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
|
|
14
16
|
|
|
@@ -40,6 +42,30 @@ module SourceMonitor
|
|
|
40
42
|
TOAST_DURATION_DEFAULT = 5000
|
|
41
43
|
TOAST_DURATION_ERROR = 6000
|
|
42
44
|
|
|
45
|
+
# Fail-closed guard: when the host app has configured no authentication or
|
|
46
|
+
# authorization handler and has not explicitly opted into open access, deny
|
|
47
|
+
# all engine routes. Configured handlers short-circuit this and decide for
|
|
48
|
+
# themselves (see SourceMonitor::Security::Authentication).
|
|
49
|
+
def enforce_source_monitor_access_default
|
|
50
|
+
return unless SourceMonitor::Security::Authentication.access_denied_by_default?(self)
|
|
51
|
+
|
|
52
|
+
source_monitor_access_forbidden
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def source_monitor_access_forbidden
|
|
56
|
+
message = "SourceMonitor access is not configured"
|
|
57
|
+
respond_to do |format|
|
|
58
|
+
format.html { render plain: message, status: :forbidden }
|
|
59
|
+
format.turbo_stream do
|
|
60
|
+
render turbo_stream: turbo_stream.append("flash",
|
|
61
|
+
partial: "source_monitor/shared/toast",
|
|
62
|
+
locals: { message: message, level: :error }),
|
|
63
|
+
status: :forbidden
|
|
64
|
+
end
|
|
65
|
+
format.json { render json: { error: message }, status: :forbidden }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
43
69
|
def authenticate_source_monitor_user
|
|
44
70
|
SourceMonitor::Security::Authentication.authenticate!(self)
|
|
45
71
|
end
|
|
@@ -60,22 +86,55 @@ module SourceMonitor
|
|
|
60
86
|
level.to_sym == :error ? TOAST_DURATION_ERROR : TOAST_DURATION_DEFAULT
|
|
61
87
|
end
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
89
|
+
# Request flashes are delivered response-local, never broadcast over the
|
|
90
|
+
# global ActionCable notification stream. On full-page HTML loads the layout
|
|
91
|
+
# renders the toasts inline (see +source_monitor_flash_toasts+); on
|
|
92
|
+
# turbo_stream responses we append the toasts to the response body so they
|
|
93
|
+
# reach only the requesting tab.
|
|
94
|
+
def source_monitor_flash_toasts
|
|
95
|
+
payloads = flash_toast_payloads
|
|
96
|
+
# Reading via the layout consumes the flash for this request so it does
|
|
97
|
+
# not linger into the next one.
|
|
98
|
+
flash.discard unless payloads.empty?
|
|
99
|
+
payloads
|
|
100
|
+
end
|
|
66
101
|
|
|
67
|
-
|
|
68
|
-
|
|
102
|
+
def append_flash_toasts_to_turbo_stream
|
|
103
|
+
return unless request.format.turbo_stream?
|
|
104
|
+
return if response.redirect?
|
|
105
|
+
|
|
106
|
+
payloads = flash_toast_payloads
|
|
107
|
+
return if payloads.empty?
|
|
108
|
+
|
|
109
|
+
streams = payloads.map do |payload|
|
|
110
|
+
view_context.turbo_stream.append(
|
|
111
|
+
"source_monitor_notifications",
|
|
112
|
+
partial: "source_monitor/shared/toast",
|
|
113
|
+
locals: {
|
|
114
|
+
message: payload[:message],
|
|
115
|
+
level: payload[:level],
|
|
116
|
+
delay_ms: toast_delay_for(payload[:level])
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
end
|
|
69
120
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
121
|
+
response.body = "#{response.body}#{streams.join}"
|
|
122
|
+
flash.discard
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Builds the list of toast payloads ({ message:, level: }) for the current
|
|
126
|
+
# request's flash. Used by both the inline layout renderer and the
|
|
127
|
+
# turbo_stream after_action so request flashes stay response-local.
|
|
128
|
+
def flash_toast_payloads
|
|
129
|
+
return [] if flash.empty?
|
|
130
|
+
|
|
131
|
+
flash.flat_map do |key, message|
|
|
132
|
+
Array(message).filter_map do |msg|
|
|
133
|
+
next if msg.blank?
|
|
134
|
+
|
|
135
|
+
{ message: msg, level: FLASH_LEVELS[key.to_sym] || :info }
|
|
75
136
|
end
|
|
76
137
|
end
|
|
77
|
-
ensure
|
|
78
|
-
flash.discard
|
|
79
138
|
end
|
|
80
139
|
end
|
|
81
140
|
end
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
<div id="source_monitor_notifications"
|
|
20
20
|
data-notification-container-target="list"
|
|
21
21
|
class="flex w-full flex-col gap-3">
|
|
22
|
+
<%# Request flashes render response-local here (never broadcast to all tabs). %>
|
|
23
|
+
<% source_monitor_flash_toasts.each do |toast| %>
|
|
24
|
+
<%= render "source_monitor/shared/toast",
|
|
25
|
+
message: toast[:message],
|
|
26
|
+
level: toast[:level] %>
|
|
27
|
+
<% end %>
|
|
22
28
|
</div>
|
|
23
29
|
<div data-notification-container-target="badge"
|
|
24
30
|
class="pointer-events-auto hidden">
|
data/docs/configuration.md
CHANGED
|
@@ -137,6 +137,11 @@ Call `config.realtime.action_cable_config` if you need a full hash for environme
|
|
|
137
137
|
|
|
138
138
|
## Authentication Helpers
|
|
139
139
|
|
|
140
|
+
**Fail-closed by default.** SourceMonitor denies access to every engine route
|
|
141
|
+
(returning `403 Forbidden`) unless you configure an authentication or
|
|
142
|
+
authorization handler. This prevents the engine's create/update/delete/enqueue
|
|
143
|
+
routes from being public by accident.
|
|
144
|
+
|
|
140
145
|
Protect the dashboard with host-specific auth in one place:
|
|
141
146
|
|
|
142
147
|
```ruby
|
|
@@ -148,7 +153,19 @@ config.authentication.current_user_method = :current_user
|
|
|
148
153
|
config.authentication.user_signed_in_method = :user_signed_in?
|
|
149
154
|
```
|
|
150
155
|
|
|
151
|
-
Handlers can be symbols (invoked on the controller) or callables. Return `false` or raise to deny access.
|
|
156
|
+
Handlers can be symbols (invoked on the controller) or callables. Return `false` or raise to deny access. As soon as either handler is configured, the handler decides access and the fail-closed guard no longer applies.
|
|
157
|
+
|
|
158
|
+
### Open access opt-in (non-production)
|
|
159
|
+
|
|
160
|
+
For local demos or sandboxes where engine routes are deliberately public, you
|
|
161
|
+
can explicitly opt out of the fail-closed guard:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
config.authentication.open_access = true # default: false
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This is intended for non-production/demo environments only. Configuring a
|
|
168
|
+
handler always takes precedence over this flag.
|
|
152
169
|
|
|
153
170
|
## Health Model
|
|
154
171
|
|
data/docs/deployment.md
CHANGED
|
@@ -33,7 +33,7 @@ SourceMonitor assumes the standard Rails 8 process split:
|
|
|
33
33
|
|
|
34
34
|
## Security & Authentication
|
|
35
35
|
|
|
36
|
-
- Lock down the
|
|
36
|
+
- SourceMonitor is **fail-closed by default**: without a configured handler every engine route returns `403 Forbidden`. Lock down the routes with authentication hooks (`config.authentication.authenticate_with` / `authorize_with`). Only set `config.authentication.open_access = true` for non-production demos where public access is intentional.
|
|
37
37
|
- Configure HTTPS for Action Cable if you expose Solid Cable over the public internet.
|
|
38
38
|
- Store API keys for authenticated feeds in encrypted credentials and inject them via per-source custom headers.
|
|
39
39
|
|