source_monitor 0.1.1
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/.gitignore +16 -0
- data/.rubocop.yml +12 -0
- data/.ruby-version +1 -0
- data/AGENTS.md +132 -0
- data/CHANGELOG.md +66 -0
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +411 -0
- data/MIT-LICENSE +20 -0
- data/README.md +108 -0
- data/Rakefile +8 -0
- data/app/assets/builds/.keep +0 -0
- data/app/assets/config/source_monitor_manifest.js +4 -0
- data/app/assets/images/source_monitor/.keep +0 -0
- data/app/assets/javascripts/source_monitor/application.js +20 -0
- data/app/assets/javascripts/source_monitor/controllers/async_submit_controller.js +36 -0
- data/app/assets/javascripts/source_monitor/controllers/dropdown_controller.js +109 -0
- data/app/assets/javascripts/source_monitor/controllers/modal_controller.js +56 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +53 -0
- data/app/assets/javascripts/source_monitor/turbo_actions.js +13 -0
- data/app/assets/stylesheets/source_monitor/application.tailwind.css +13 -0
- data/app/assets/svgs/source_monitor/.keep +0 -0
- data/app/controllers/concerns/.keep +0 -0
- data/app/controllers/concerns/source_monitor/sanitizes_search_params.rb +81 -0
- data/app/controllers/source_monitor/application_controller.rb +62 -0
- data/app/controllers/source_monitor/dashboard_controller.rb +27 -0
- data/app/controllers/source_monitor/fetch_logs_controller.rb +9 -0
- data/app/controllers/source_monitor/health_controller.rb +10 -0
- data/app/controllers/source_monitor/items_controller.rb +116 -0
- data/app/controllers/source_monitor/logs_controller.rb +15 -0
- data/app/controllers/source_monitor/scrape_logs_controller.rb +9 -0
- data/app/controllers/source_monitor/source_bulk_scrapes_controller.rb +35 -0
- data/app/controllers/source_monitor/source_fetches_controller.rb +22 -0
- data/app/controllers/source_monitor/source_health_checks_controller.rb +34 -0
- data/app/controllers/source_monitor/source_health_resets_controller.rb +27 -0
- data/app/controllers/source_monitor/source_retries_controller.rb +22 -0
- data/app/controllers/source_monitor/source_turbo_responses.rb +115 -0
- data/app/controllers/source_monitor/sources_controller.rb +179 -0
- data/app/helpers/source_monitor/application_helper.rb +327 -0
- data/app/jobs/source_monitor/application_job.rb +13 -0
- data/app/jobs/source_monitor/fetch_feed_job.rb +117 -0
- data/app/jobs/source_monitor/item_cleanup_job.rb +48 -0
- data/app/jobs/source_monitor/log_cleanup_job.rb +47 -0
- data/app/jobs/source_monitor/schedule_fetches_job.rb +29 -0
- data/app/jobs/source_monitor/scrape_item_job.rb +47 -0
- data/app/jobs/source_monitor/source_health_check_job.rb +77 -0
- data/app/mailers/source_monitor/application_mailer.rb +17 -0
- data/app/models/concerns/.keep +0 -0
- data/app/models/concerns/source_monitor/loggable.rb +18 -0
- data/app/models/source_monitor/application_record.rb +5 -0
- data/app/models/source_monitor/fetch_log.rb +31 -0
- data/app/models/source_monitor/health_check_log.rb +28 -0
- data/app/models/source_monitor/item.rb +102 -0
- data/app/models/source_monitor/item_content.rb +11 -0
- data/app/models/source_monitor/log_entry.rb +56 -0
- data/app/models/source_monitor/scrape_log.rb +31 -0
- data/app/models/source_monitor/source.rb +115 -0
- data/app/views/layouts/source_monitor/application.html.erb +54 -0
- data/app/views/source_monitor/dashboard/_fetch_schedule.html.erb +90 -0
- data/app/views/source_monitor/dashboard/_job_metrics.html.erb +82 -0
- data/app/views/source_monitor/dashboard/_recent_activity.html.erb +39 -0
- data/app/views/source_monitor/dashboard/_stat_card.html.erb +6 -0
- data/app/views/source_monitor/dashboard/_stats.html.erb +9 -0
- data/app/views/source_monitor/dashboard/index.html.erb +48 -0
- data/app/views/source_monitor/fetch_logs/show.html.erb +90 -0
- data/app/views/source_monitor/items/_details.html.erb +234 -0
- data/app/views/source_monitor/items/_details_wrapper.html.erb +3 -0
- data/app/views/source_monitor/items/index.html.erb +147 -0
- data/app/views/source_monitor/items/show.html.erb +3 -0
- data/app/views/source_monitor/logs/index.html.erb +208 -0
- data/app/views/source_monitor/scrape_logs/show.html.erb +73 -0
- data/app/views/source_monitor/shared/_toast.html.erb +34 -0
- data/app/views/source_monitor/sources/_bulk_scrape_form.html.erb +64 -0
- data/app/views/source_monitor/sources/_bulk_scrape_modal.html.erb +53 -0
- data/app/views/source_monitor/sources/_details.html.erb +302 -0
- data/app/views/source_monitor/sources/_details_wrapper.html.erb +3 -0
- data/app/views/source_monitor/sources/_empty_state_row.html.erb +5 -0
- data/app/views/source_monitor/sources/_fetch_interval_heatmap.html.erb +46 -0
- data/app/views/source_monitor/sources/_form.html.erb +143 -0
- data/app/views/source_monitor/sources/_health_status_badge.html.erb +46 -0
- data/app/views/source_monitor/sources/_row.html.erb +102 -0
- data/app/views/source_monitor/sources/edit.html.erb +28 -0
- data/app/views/source_monitor/sources/index.html.erb +153 -0
- data/app/views/source_monitor/sources/new.html.erb +22 -0
- data/app/views/source_monitor/sources/show.html.erb +3 -0
- data/config/coverage_baseline.json +2010 -0
- data/config/initializers/feedjira.rb +19 -0
- data/config/routes.rb +18 -0
- data/config/tailwind.config.js +17 -0
- data/db/migrate/20241008120000_create_source_monitor_sources.rb +40 -0
- data/db/migrate/20241008121000_create_source_monitor_items.rb +44 -0
- data/db/migrate/20241008122000_create_source_monitor_fetch_logs.rb +32 -0
- data/db/migrate/20241008123000_create_source_monitor_scrape_logs.rb +25 -0
- data/db/migrate/20251008183000_change_fetch_interval_to_minutes.rb +23 -0
- data/db/migrate/20251009090000_create_source_monitor_item_contents.rb +38 -0
- data/db/migrate/20251009103000_add_feed_content_readability_to_sources.rb +5 -0
- data/db/migrate/20251010090000_add_adaptive_fetching_toggle_to_sources.rb +7 -0
- data/db/migrate/20251010123000_add_deleted_at_to_source_monitor_items.rb +8 -0
- data/db/migrate/20251010153000_add_type_to_source_monitor_sources.rb +8 -0
- data/db/migrate/20251010154500_add_fetch_status_to_source_monitor_sources.rb +9 -0
- data/db/migrate/20251010160000_create_solid_cable_messages.rb +16 -0
- data/db/migrate/20251011090000_add_fetch_retry_state_to_sources.rb +14 -0
- data/db/migrate/20251012090000_add_health_fields_to_sources.rb +17 -0
- data/db/migrate/20251012100000_optimize_source_monitor_database_performance.rb +13 -0
- data/db/migrate/20251014064947_add_not_null_constraints_to_items.rb +30 -0
- data/db/migrate/20251014171659_add_performance_indexes.rb +29 -0
- data/db/migrate/20251014172525_add_fetch_status_check_constraint.rb +18 -0
- data/db/migrate/20251015100000_create_source_monitor_log_entries.rb +89 -0
- data/db/migrate/20251022100000_create_source_monitor_health_check_logs.rb +22 -0
- data/db/migrate/20251108120116_refresh_fetch_status_constraint.rb +29 -0
- data/docs/configuration.md +170 -0
- data/docs/deployment.md +63 -0
- data/docs/gh-cli-workflow.md +44 -0
- data/docs/installation.md +144 -0
- data/docs/troubleshooting.md +76 -0
- data/eslint.config.mjs +27 -0
- data/lib/generators/source_monitor/install/install_generator.rb +59 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +155 -0
- data/lib/source_monitor/analytics/source_activity_rates.rb +53 -0
- data/lib/source_monitor/analytics/source_fetch_interval_distribution.rb +57 -0
- data/lib/source_monitor/analytics/sources_index_metrics.rb +92 -0
- data/lib/source_monitor/assets/bundler.rb +49 -0
- data/lib/source_monitor/assets.rb +6 -0
- data/lib/source_monitor/configuration.rb +654 -0
- data/lib/source_monitor/dashboard/queries.rb +356 -0
- data/lib/source_monitor/dashboard/quick_action.rb +7 -0
- data/lib/source_monitor/dashboard/quick_actions_presenter.rb +26 -0
- data/lib/source_monitor/dashboard/recent_activity.rb +30 -0
- data/lib/source_monitor/dashboard/recent_activity_presenter.rb +77 -0
- data/lib/source_monitor/dashboard/turbo_broadcaster.rb +87 -0
- data/lib/source_monitor/dashboard/upcoming_fetch_schedule.rb +126 -0
- data/lib/source_monitor/engine.rb +107 -0
- data/lib/source_monitor/events.rb +110 -0
- data/lib/source_monitor/feedjira_extensions.rb +103 -0
- data/lib/source_monitor/fetching/advisory_lock.rb +54 -0
- data/lib/source_monitor/fetching/completion/event_publisher.rb +22 -0
- data/lib/source_monitor/fetching/completion/follow_up_handler.rb +37 -0
- data/lib/source_monitor/fetching/completion/retention_handler.rb +30 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +627 -0
- data/lib/source_monitor/fetching/fetch_error.rb +88 -0
- data/lib/source_monitor/fetching/fetch_runner.rb +142 -0
- data/lib/source_monitor/fetching/retry_policy.rb +85 -0
- data/lib/source_monitor/fetching/stalled_fetch_reconciler.rb +146 -0
- data/lib/source_monitor/health/source_health_check.rb +100 -0
- data/lib/source_monitor/health/source_health_monitor.rb +210 -0
- data/lib/source_monitor/health/source_health_reset.rb +68 -0
- data/lib/source_monitor/health.rb +46 -0
- data/lib/source_monitor/http.rb +85 -0
- data/lib/source_monitor/instrumentation.rb +52 -0
- data/lib/source_monitor/items/item_creator.rb +601 -0
- data/lib/source_monitor/items/retention_pruner.rb +146 -0
- data/lib/source_monitor/items/retention_strategies/destroy.rb +26 -0
- data/lib/source_monitor/items/retention_strategies/soft_delete.rb +50 -0
- data/lib/source_monitor/items/retention_strategies.rb +9 -0
- data/lib/source_monitor/jobs/cleanup_options.rb +85 -0
- data/lib/source_monitor/jobs/fetch_failure_subscriber.rb +129 -0
- data/lib/source_monitor/jobs/solid_queue_metrics.rb +199 -0
- data/lib/source_monitor/jobs/visibility.rb +133 -0
- data/lib/source_monitor/logs/entry_sync.rb +69 -0
- data/lib/source_monitor/logs/filter_set.rb +163 -0
- data/lib/source_monitor/logs/query.rb +81 -0
- data/lib/source_monitor/logs/table_presenter.rb +161 -0
- data/lib/source_monitor/metrics.rb +77 -0
- data/lib/source_monitor/model_extensions.rb +109 -0
- data/lib/source_monitor/models/sanitizable.rb +76 -0
- data/lib/source_monitor/models/url_normalizable.rb +84 -0
- data/lib/source_monitor/pagination/paginator.rb +90 -0
- data/lib/source_monitor/realtime/adapter.rb +97 -0
- data/lib/source_monitor/realtime/broadcaster.rb +237 -0
- data/lib/source_monitor/realtime.rb +17 -0
- data/lib/source_monitor/release/changelog.rb +59 -0
- data/lib/source_monitor/release/runner.rb +73 -0
- data/lib/source_monitor/scheduler.rb +82 -0
- data/lib/source_monitor/scrapers/base.rb +105 -0
- data/lib/source_monitor/scrapers/fetchers/http_fetcher.rb +97 -0
- data/lib/source_monitor/scrapers/parsers/readability_parser.rb +101 -0
- data/lib/source_monitor/scrapers/readability.rb +156 -0
- data/lib/source_monitor/scraping/bulk_result_presenter.rb +85 -0
- data/lib/source_monitor/scraping/bulk_source_scraper.rb +233 -0
- data/lib/source_monitor/scraping/enqueuer.rb +125 -0
- data/lib/source_monitor/scraping/item_scraper/adapter_resolver.rb +44 -0
- data/lib/source_monitor/scraping/item_scraper/persistence.rb +189 -0
- data/lib/source_monitor/scraping/item_scraper.rb +84 -0
- data/lib/source_monitor/scraping/scheduler.rb +43 -0
- data/lib/source_monitor/scraping/state.rb +79 -0
- data/lib/source_monitor/security/authentication.rb +85 -0
- data/lib/source_monitor/security/parameter_sanitizer.rb +42 -0
- data/lib/source_monitor/sources/turbo_stream_presenter.rb +54 -0
- data/lib/source_monitor/turbo_streams/stream_responder.rb +95 -0
- data/lib/source_monitor/version.rb +3 -0
- data/lib/source_monitor.rb +149 -0
- data/lib/tasks/recover_stalled_fetches.rake +16 -0
- data/lib/tasks/source_monitor_assets.rake +28 -0
- data/lib/tasks/source_monitor_tasks.rake +29 -0
- data/lib/tasks/test_smoke.rake +12 -0
- data/package-lock.json +3997 -0
- data/package.json +29 -0
- data/postcss.config.js +6 -0
- data/source_monitor.gemspec +46 -0
- data/stylelint.config.js +12 -0
- metadata +469 -0
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
source_monitor (0.1.1)
|
|
5
|
+
cssbundling-rails (~> 1.4)
|
|
6
|
+
faraday (~> 2.9)
|
|
7
|
+
faraday-follow_redirects (~> 0.4)
|
|
8
|
+
faraday-gzip (~> 3.0)
|
|
9
|
+
faraday-retry (~> 2.2)
|
|
10
|
+
feedjira (>= 3.2, < 5.0)
|
|
11
|
+
jsbundling-rails (~> 1.3)
|
|
12
|
+
nokolexbor (~> 0.5)
|
|
13
|
+
rails (>= 8.0.3, < 9.0)
|
|
14
|
+
ransack (~> 4.2)
|
|
15
|
+
ruby-readability (~> 0.7)
|
|
16
|
+
solid_cable (>= 3.0, < 4.0)
|
|
17
|
+
solid_queue (>= 0.3, < 3.0)
|
|
18
|
+
turbo-rails (~> 2.0)
|
|
19
|
+
|
|
20
|
+
GEM
|
|
21
|
+
remote: https://rubygems.org/
|
|
22
|
+
specs:
|
|
23
|
+
action_text-trix (2.1.15)
|
|
24
|
+
railties
|
|
25
|
+
actioncable (8.1.1)
|
|
26
|
+
actionpack (= 8.1.1)
|
|
27
|
+
activesupport (= 8.1.1)
|
|
28
|
+
nio4r (~> 2.0)
|
|
29
|
+
websocket-driver (>= 0.6.1)
|
|
30
|
+
zeitwerk (~> 2.6)
|
|
31
|
+
actionmailbox (8.1.1)
|
|
32
|
+
actionpack (= 8.1.1)
|
|
33
|
+
activejob (= 8.1.1)
|
|
34
|
+
activerecord (= 8.1.1)
|
|
35
|
+
activestorage (= 8.1.1)
|
|
36
|
+
activesupport (= 8.1.1)
|
|
37
|
+
mail (>= 2.8.0)
|
|
38
|
+
actionmailer (8.1.1)
|
|
39
|
+
actionpack (= 8.1.1)
|
|
40
|
+
actionview (= 8.1.1)
|
|
41
|
+
activejob (= 8.1.1)
|
|
42
|
+
activesupport (= 8.1.1)
|
|
43
|
+
mail (>= 2.8.0)
|
|
44
|
+
rails-dom-testing (~> 2.2)
|
|
45
|
+
actionpack (8.1.1)
|
|
46
|
+
actionview (= 8.1.1)
|
|
47
|
+
activesupport (= 8.1.1)
|
|
48
|
+
nokogiri (>= 1.8.5)
|
|
49
|
+
rack (>= 2.2.4)
|
|
50
|
+
rack-session (>= 1.0.1)
|
|
51
|
+
rack-test (>= 0.6.3)
|
|
52
|
+
rails-dom-testing (~> 2.2)
|
|
53
|
+
rails-html-sanitizer (~> 1.6)
|
|
54
|
+
useragent (~> 0.16)
|
|
55
|
+
actiontext (8.1.1)
|
|
56
|
+
action_text-trix (~> 2.1.15)
|
|
57
|
+
actionpack (= 8.1.1)
|
|
58
|
+
activerecord (= 8.1.1)
|
|
59
|
+
activestorage (= 8.1.1)
|
|
60
|
+
activesupport (= 8.1.1)
|
|
61
|
+
globalid (>= 0.6.0)
|
|
62
|
+
nokogiri (>= 1.8.5)
|
|
63
|
+
actionview (8.1.1)
|
|
64
|
+
activesupport (= 8.1.1)
|
|
65
|
+
builder (~> 3.1)
|
|
66
|
+
erubi (~> 1.11)
|
|
67
|
+
rails-dom-testing (~> 2.2)
|
|
68
|
+
rails-html-sanitizer (~> 1.6)
|
|
69
|
+
activejob (8.1.1)
|
|
70
|
+
activesupport (= 8.1.1)
|
|
71
|
+
globalid (>= 0.3.6)
|
|
72
|
+
activemodel (8.1.1)
|
|
73
|
+
activesupport (= 8.1.1)
|
|
74
|
+
activerecord (8.1.1)
|
|
75
|
+
activemodel (= 8.1.1)
|
|
76
|
+
activesupport (= 8.1.1)
|
|
77
|
+
timeout (>= 0.4.0)
|
|
78
|
+
activestorage (8.1.1)
|
|
79
|
+
actionpack (= 8.1.1)
|
|
80
|
+
activejob (= 8.1.1)
|
|
81
|
+
activerecord (= 8.1.1)
|
|
82
|
+
activesupport (= 8.1.1)
|
|
83
|
+
marcel (~> 1.0)
|
|
84
|
+
activesupport (8.1.1)
|
|
85
|
+
base64
|
|
86
|
+
bigdecimal
|
|
87
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
88
|
+
connection_pool (>= 2.2.5)
|
|
89
|
+
drb
|
|
90
|
+
i18n (>= 1.6, < 2)
|
|
91
|
+
json
|
|
92
|
+
logger (>= 1.4.2)
|
|
93
|
+
minitest (>= 5.1)
|
|
94
|
+
securerandom (>= 0.3)
|
|
95
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
96
|
+
uri (>= 0.13.1)
|
|
97
|
+
addressable (2.8.7)
|
|
98
|
+
public_suffix (>= 2.0.2, < 7.0)
|
|
99
|
+
ast (2.4.3)
|
|
100
|
+
base64 (0.3.0)
|
|
101
|
+
bigdecimal (3.3.1)
|
|
102
|
+
brakeman (7.1.0)
|
|
103
|
+
racc
|
|
104
|
+
builder (3.3.0)
|
|
105
|
+
capybara (3.40.0)
|
|
106
|
+
addressable
|
|
107
|
+
matrix
|
|
108
|
+
mini_mime (>= 0.1.3)
|
|
109
|
+
nokogiri (~> 1.11)
|
|
110
|
+
rack (>= 1.6.0)
|
|
111
|
+
rack-test (>= 0.6.3)
|
|
112
|
+
regexp_parser (>= 1.5, < 3.0)
|
|
113
|
+
xpath (~> 3.2)
|
|
114
|
+
concurrent-ruby (1.3.5)
|
|
115
|
+
connection_pool (2.5.4)
|
|
116
|
+
crack (1.0.1)
|
|
117
|
+
bigdecimal
|
|
118
|
+
rexml
|
|
119
|
+
crass (1.0.6)
|
|
120
|
+
cssbundling-rails (1.4.3)
|
|
121
|
+
railties (>= 6.0.0)
|
|
122
|
+
date (3.5.0)
|
|
123
|
+
docile (1.4.1)
|
|
124
|
+
drb (2.2.3)
|
|
125
|
+
erb (5.1.3)
|
|
126
|
+
erubi (1.13.1)
|
|
127
|
+
et-orbi (1.4.0)
|
|
128
|
+
tzinfo
|
|
129
|
+
faraday (2.14.0)
|
|
130
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
131
|
+
json
|
|
132
|
+
logger
|
|
133
|
+
faraday-follow_redirects (0.4.0)
|
|
134
|
+
faraday (>= 1, < 3)
|
|
135
|
+
faraday-gzip (3.0.4)
|
|
136
|
+
faraday (>= 2.0, < 3)
|
|
137
|
+
zlib (~> 3.0)
|
|
138
|
+
faraday-net_http (3.4.1)
|
|
139
|
+
net-http (>= 0.5.0)
|
|
140
|
+
faraday-retry (2.3.2)
|
|
141
|
+
faraday (~> 2.0)
|
|
142
|
+
feedjira (4.0.1)
|
|
143
|
+
logger (>= 1.0, < 2)
|
|
144
|
+
loofah (>= 2.3.1, < 3)
|
|
145
|
+
sax-machine (>= 1.0, < 2)
|
|
146
|
+
fugit (1.12.1)
|
|
147
|
+
et-orbi (~> 1.4)
|
|
148
|
+
raabro (~> 1.4)
|
|
149
|
+
globalid (1.3.0)
|
|
150
|
+
activesupport (>= 6.1)
|
|
151
|
+
guess_html_encoding (0.0.11)
|
|
152
|
+
hashdiff (1.2.1)
|
|
153
|
+
i18n (1.14.7)
|
|
154
|
+
concurrent-ruby (~> 1.0)
|
|
155
|
+
io-console (0.8.1)
|
|
156
|
+
irb (1.15.3)
|
|
157
|
+
pp (>= 0.6.0)
|
|
158
|
+
rdoc (>= 4.0.0)
|
|
159
|
+
reline (>= 0.4.2)
|
|
160
|
+
jsbundling-rails (1.3.1)
|
|
161
|
+
railties (>= 6.0.0)
|
|
162
|
+
json (2.15.2)
|
|
163
|
+
language_server-protocol (3.17.0.5)
|
|
164
|
+
lint_roller (1.1.0)
|
|
165
|
+
logger (1.7.0)
|
|
166
|
+
loofah (2.24.1)
|
|
167
|
+
crass (~> 1.0.2)
|
|
168
|
+
nokogiri (>= 1.12.0)
|
|
169
|
+
mail (2.8.1)
|
|
170
|
+
mini_mime (>= 0.1.1)
|
|
171
|
+
net-imap
|
|
172
|
+
net-pop
|
|
173
|
+
net-smtp
|
|
174
|
+
marcel (1.1.0)
|
|
175
|
+
matrix (0.4.3)
|
|
176
|
+
mini_mime (1.1.5)
|
|
177
|
+
mini_portile2 (2.8.9)
|
|
178
|
+
minitest (5.26.0)
|
|
179
|
+
net-http (0.6.0)
|
|
180
|
+
uri
|
|
181
|
+
net-imap (0.5.12)
|
|
182
|
+
date
|
|
183
|
+
net-protocol
|
|
184
|
+
net-pop (0.1.2)
|
|
185
|
+
net-protocol
|
|
186
|
+
net-protocol (0.2.2)
|
|
187
|
+
timeout
|
|
188
|
+
net-smtp (0.5.1)
|
|
189
|
+
net-protocol
|
|
190
|
+
nio4r (2.7.4)
|
|
191
|
+
nokogiri (1.18.10)
|
|
192
|
+
mini_portile2 (~> 2.8.2)
|
|
193
|
+
racc (~> 1.4)
|
|
194
|
+
nokogiri (1.18.10-aarch64-linux-gnu)
|
|
195
|
+
racc (~> 1.4)
|
|
196
|
+
nokogiri (1.18.10-aarch64-linux-musl)
|
|
197
|
+
racc (~> 1.4)
|
|
198
|
+
nokogiri (1.18.10-arm-linux-gnu)
|
|
199
|
+
racc (~> 1.4)
|
|
200
|
+
nokogiri (1.18.10-arm-linux-musl)
|
|
201
|
+
racc (~> 1.4)
|
|
202
|
+
nokogiri (1.18.10-arm64-darwin)
|
|
203
|
+
racc (~> 1.4)
|
|
204
|
+
nokogiri (1.18.10-x86_64-darwin)
|
|
205
|
+
racc (~> 1.4)
|
|
206
|
+
nokogiri (1.18.10-x86_64-linux-gnu)
|
|
207
|
+
racc (~> 1.4)
|
|
208
|
+
nokogiri (1.18.10-x86_64-linux-musl)
|
|
209
|
+
racc (~> 1.4)
|
|
210
|
+
nokolexbor (0.6.2)
|
|
211
|
+
nokolexbor (0.6.2-arm64-darwin)
|
|
212
|
+
nokolexbor (0.6.2-x86_64-darwin)
|
|
213
|
+
nokolexbor (0.6.2-x86_64-linux)
|
|
214
|
+
parallel (1.27.0)
|
|
215
|
+
parser (3.3.9.0)
|
|
216
|
+
ast (~> 2.4.1)
|
|
217
|
+
racc
|
|
218
|
+
pg (1.6.2)
|
|
219
|
+
pg (1.6.2-aarch64-linux)
|
|
220
|
+
pg (1.6.2-aarch64-linux-musl)
|
|
221
|
+
pg (1.6.2-arm64-darwin)
|
|
222
|
+
pg (1.6.2-x86_64-darwin)
|
|
223
|
+
pg (1.6.2-x86_64-linux)
|
|
224
|
+
pg (1.6.2-x86_64-linux-musl)
|
|
225
|
+
pp (0.6.3)
|
|
226
|
+
prettyprint
|
|
227
|
+
prettyprint (0.2.0)
|
|
228
|
+
prism (1.5.1)
|
|
229
|
+
propshaft (1.3.1)
|
|
230
|
+
actionpack (>= 7.0.0)
|
|
231
|
+
activesupport (>= 7.0.0)
|
|
232
|
+
rack
|
|
233
|
+
psych (5.2.6)
|
|
234
|
+
date
|
|
235
|
+
stringio
|
|
236
|
+
public_suffix (6.0.2)
|
|
237
|
+
puma (7.1.0)
|
|
238
|
+
nio4r (~> 2.0)
|
|
239
|
+
raabro (1.4.0)
|
|
240
|
+
racc (1.8.1)
|
|
241
|
+
rack (3.2.4)
|
|
242
|
+
rack-session (2.1.1)
|
|
243
|
+
base64 (>= 0.1.0)
|
|
244
|
+
rack (>= 3.0.0)
|
|
245
|
+
rack-test (2.2.0)
|
|
246
|
+
rack (>= 1.3)
|
|
247
|
+
rackup (2.2.1)
|
|
248
|
+
rack (>= 3)
|
|
249
|
+
rails (8.1.1)
|
|
250
|
+
actioncable (= 8.1.1)
|
|
251
|
+
actionmailbox (= 8.1.1)
|
|
252
|
+
actionmailer (= 8.1.1)
|
|
253
|
+
actionpack (= 8.1.1)
|
|
254
|
+
actiontext (= 8.1.1)
|
|
255
|
+
actionview (= 8.1.1)
|
|
256
|
+
activejob (= 8.1.1)
|
|
257
|
+
activemodel (= 8.1.1)
|
|
258
|
+
activerecord (= 8.1.1)
|
|
259
|
+
activestorage (= 8.1.1)
|
|
260
|
+
activesupport (= 8.1.1)
|
|
261
|
+
bundler (>= 1.15.0)
|
|
262
|
+
railties (= 8.1.1)
|
|
263
|
+
rails-dom-testing (2.3.0)
|
|
264
|
+
activesupport (>= 5.0.0)
|
|
265
|
+
minitest
|
|
266
|
+
nokogiri (>= 1.6)
|
|
267
|
+
rails-html-sanitizer (1.6.2)
|
|
268
|
+
loofah (~> 2.21)
|
|
269
|
+
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)
|
|
270
|
+
railties (8.1.1)
|
|
271
|
+
actionpack (= 8.1.1)
|
|
272
|
+
activesupport (= 8.1.1)
|
|
273
|
+
irb (~> 1.13)
|
|
274
|
+
rackup (>= 1.0.0)
|
|
275
|
+
rake (>= 12.2)
|
|
276
|
+
thor (~> 1.0, >= 1.2.2)
|
|
277
|
+
tsort (>= 0.2)
|
|
278
|
+
zeitwerk (~> 2.6)
|
|
279
|
+
rainbow (3.1.1)
|
|
280
|
+
rake (13.3.1)
|
|
281
|
+
ransack (4.4.1)
|
|
282
|
+
activerecord (>= 7.2)
|
|
283
|
+
activesupport (>= 7.2)
|
|
284
|
+
i18n
|
|
285
|
+
rdoc (6.15.1)
|
|
286
|
+
erb
|
|
287
|
+
psych (>= 4.0.0)
|
|
288
|
+
tsort
|
|
289
|
+
regexp_parser (2.11.3)
|
|
290
|
+
reline (0.6.2)
|
|
291
|
+
io-console (~> 0.5)
|
|
292
|
+
rexml (3.4.4)
|
|
293
|
+
rubocop (1.81.1)
|
|
294
|
+
json (~> 2.3)
|
|
295
|
+
language_server-protocol (~> 3.17.0.2)
|
|
296
|
+
lint_roller (~> 1.1.0)
|
|
297
|
+
parallel (~> 1.10)
|
|
298
|
+
parser (>= 3.3.0.2)
|
|
299
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
300
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
301
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
302
|
+
ruby-progressbar (~> 1.7)
|
|
303
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
304
|
+
rubocop-ast (1.47.1)
|
|
305
|
+
parser (>= 3.3.7.2)
|
|
306
|
+
prism (~> 1.4)
|
|
307
|
+
rubocop-performance (1.26.0)
|
|
308
|
+
lint_roller (~> 1.1)
|
|
309
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
310
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
|
311
|
+
rubocop-rails (2.33.4)
|
|
312
|
+
activesupport (>= 4.2.0)
|
|
313
|
+
lint_roller (~> 1.1)
|
|
314
|
+
rack (>= 1.1)
|
|
315
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
316
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
|
317
|
+
rubocop-rails-omakase (1.1.0)
|
|
318
|
+
rubocop (>= 1.72)
|
|
319
|
+
rubocop-performance (>= 1.24)
|
|
320
|
+
rubocop-rails (>= 2.30)
|
|
321
|
+
ruby-progressbar (1.13.0)
|
|
322
|
+
ruby-readability (0.7.2)
|
|
323
|
+
guess_html_encoding (>= 0.0.4)
|
|
324
|
+
nokogiri (>= 1.6.0)
|
|
325
|
+
rubyzip (3.2.2)
|
|
326
|
+
sax-machine (1.3.2)
|
|
327
|
+
securerandom (0.4.1)
|
|
328
|
+
selenium-webdriver (4.38.0)
|
|
329
|
+
base64 (~> 0.2)
|
|
330
|
+
logger (~> 1.4)
|
|
331
|
+
rexml (~> 3.2, >= 3.2.5)
|
|
332
|
+
rubyzip (>= 1.2.2, < 4.0)
|
|
333
|
+
websocket (~> 1.0)
|
|
334
|
+
simplecov (0.22.0)
|
|
335
|
+
docile (~> 1.1)
|
|
336
|
+
simplecov-html (~> 0.11)
|
|
337
|
+
simplecov_json_formatter (~> 0.1)
|
|
338
|
+
simplecov-html (0.13.2)
|
|
339
|
+
simplecov_json_formatter (0.1.4)
|
|
340
|
+
solid_cable (3.0.12)
|
|
341
|
+
actioncable (>= 7.2)
|
|
342
|
+
activejob (>= 7.2)
|
|
343
|
+
activerecord (>= 7.2)
|
|
344
|
+
railties (>= 7.2)
|
|
345
|
+
solid_queue (1.2.4)
|
|
346
|
+
activejob (>= 7.1)
|
|
347
|
+
activerecord (>= 7.1)
|
|
348
|
+
concurrent-ruby (>= 1.3.1)
|
|
349
|
+
fugit (~> 1.11)
|
|
350
|
+
railties (>= 7.1)
|
|
351
|
+
thor (>= 1.3.1)
|
|
352
|
+
stackprof (0.2.27)
|
|
353
|
+
stringio (3.1.7)
|
|
354
|
+
test-prof (1.4.4)
|
|
355
|
+
thor (1.4.0)
|
|
356
|
+
timeout (0.4.4)
|
|
357
|
+
tsort (0.2.0)
|
|
358
|
+
turbo-rails (2.0.20)
|
|
359
|
+
actionpack (>= 7.1.0)
|
|
360
|
+
railties (>= 7.1.0)
|
|
361
|
+
tzinfo (2.0.6)
|
|
362
|
+
concurrent-ruby (~> 1.0)
|
|
363
|
+
unicode-display_width (3.2.0)
|
|
364
|
+
unicode-emoji (~> 4.1)
|
|
365
|
+
unicode-emoji (4.1.0)
|
|
366
|
+
uri (1.1.0)
|
|
367
|
+
useragent (0.16.11)
|
|
368
|
+
vcr (6.3.1)
|
|
369
|
+
base64
|
|
370
|
+
webmock (3.26.1)
|
|
371
|
+
addressable (>= 2.8.0)
|
|
372
|
+
crack (>= 0.3.2)
|
|
373
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
374
|
+
websocket (1.2.11)
|
|
375
|
+
websocket-driver (0.8.0)
|
|
376
|
+
base64
|
|
377
|
+
websocket-extensions (>= 0.1.0)
|
|
378
|
+
websocket-extensions (0.1.5)
|
|
379
|
+
xpath (3.2.0)
|
|
380
|
+
nokogiri (~> 1.8)
|
|
381
|
+
zeitwerk (2.7.3)
|
|
382
|
+
zlib (3.2.1)
|
|
383
|
+
|
|
384
|
+
PLATFORMS
|
|
385
|
+
aarch64-linux-gnu
|
|
386
|
+
aarch64-linux-musl
|
|
387
|
+
arm-linux-gnu
|
|
388
|
+
arm-linux-musl
|
|
389
|
+
arm64-darwin
|
|
390
|
+
ruby
|
|
391
|
+
x86_64-darwin
|
|
392
|
+
x86_64-linux-gnu
|
|
393
|
+
x86_64-linux-musl
|
|
394
|
+
|
|
395
|
+
DEPENDENCIES
|
|
396
|
+
brakeman
|
|
397
|
+
capybara
|
|
398
|
+
pg
|
|
399
|
+
propshaft
|
|
400
|
+
puma
|
|
401
|
+
rubocop-rails-omakase
|
|
402
|
+
selenium-webdriver
|
|
403
|
+
simplecov
|
|
404
|
+
source_monitor!
|
|
405
|
+
stackprof
|
|
406
|
+
test-prof
|
|
407
|
+
vcr
|
|
408
|
+
webmock
|
|
409
|
+
|
|
410
|
+
BUNDLED WITH
|
|
411
|
+
2.7.2
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright dchuk
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# SourceMonitor
|
|
2
|
+
|
|
3
|
+
SourceMonitor is a production-ready Rails 8 mountable engine for ingesting, normalising, scraping, and monitoring RSS/Atom/JSON feeds. It ships with a Tailwind-powered admin UI, Solid Queue job orchestration, Solid Cable realtime broadcasting, and an extensible configuration layer so host applications can offer full-stack feed operations without rebuilding infrastructure.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
- Full-featured source and item administration backed by Turbo Streams and Tailwind UI components
|
|
7
|
+
- Adaptive fetch pipeline (Feedjira + Faraday) with conditional GETs, retention pruning, and scrape orchestration
|
|
8
|
+
- Realtime dashboard metrics, batching/caching query layer, and Mission Control integration hooks
|
|
9
|
+
- Extensible scraper adapters (Readability included) with per-source settings and structured result metadata
|
|
10
|
+
- Declarative configuration DSL covering queues, HTTP, retention, events, model extensions, authentication, and realtime transports
|
|
11
|
+
- First-class observability through ActiveSupport notifications and `SourceMonitor::Metrics` counters/gauges
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
- Ruby 3.4.4 (we recommend [rbenv](https://github.com/rbenv/rbenv) for local development: `rbenv install 3.4.4 && rbenv local 3.4.4`, but use whatever Ruby version manager suits your environment—asdf, chruby, rvm, or container-based workflows all work fine)
|
|
15
|
+
- Rails ≥ 8.0.2.1 in the host application
|
|
16
|
+
- PostgreSQL 13+ (engine migrations use JSONB, SKIP LOCKED, advisory locks, and Solid Cable tables)
|
|
17
|
+
- Node.js 18+ (npm or Yarn) for asset linting and the Tailwind/esbuild bundling pipeline
|
|
18
|
+
- Solid Queue workers (Rails 8 default) and Solid Cable (default realtime adapter)
|
|
19
|
+
- Optional: Mission Control Jobs for dashboard linking, Redis if you opt into the Redis realtime adapter
|
|
20
|
+
|
|
21
|
+
## Quick Start (Host Application)
|
|
22
|
+
|
|
23
|
+
> **Note on commands:** The examples below use bare `bundle` and `bin/rails` commands. If you use rbenv, prefix them with `rbenv exec` (e.g., `rbenv exec bundle install`). For Docker/container environments, run commands directly or via your container runtime. Adjust to match your Ruby version management approach.
|
|
24
|
+
|
|
25
|
+
### Initial Install
|
|
26
|
+
1. Add `gem "source_monitor", github: "dchuk/source_monitor"` to your Gemfile and run `bundle install`.
|
|
27
|
+
2. Install the engine: `bin/rails generate source_monitor:install --mount-path=/source_monitor` (updates routes, drops the initializer, prints doc links).
|
|
28
|
+
3. Copy migrations: `bin/rails railties:install:migrations FROM=source_monitor`.
|
|
29
|
+
4. Apply migrations: `bin/rails db:migrate` (creates sources/items/logs tables, Solid Cable messages, and Solid Queue schema when required).
|
|
30
|
+
5. Install frontend tooling if you plan to extend engine assets: `npm install`.
|
|
31
|
+
6. Start background workers: `bin/rails solid_queue:start` (or your preferred process manager).
|
|
32
|
+
7. Boot your app and visit the mount path you chose (default `/source_monitor`) to explore the dashboard and confirm Solid Queue metrics render.
|
|
33
|
+
|
|
34
|
+
Detailed instructions, optional flags, and verification steps live in [docs/installation.md](docs/installation.md), with troubleshooting advice in [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
35
|
+
|
|
36
|
+
### Upgrading SourceMonitor
|
|
37
|
+
1. Bump the gem version in your host `Gemfile` and run `bundle install` (or `bundle update source_monitor` when targeting a specific release).
|
|
38
|
+
2. Re-run `bin/rails railties:install:migrations FROM=source_monitor` and then `bin/rails db:migrate` to pick up schema changes.
|
|
39
|
+
3. Compare your `config/initializers/source_monitor.rb` against the newly generated template for configuration diffs (new queue knobs, HTTP options, etc.).
|
|
40
|
+
4. Review release notes for optional integrations—when enabling Mission Control, ensure `mission_control-jobs` stays mounted and linked via `config.mission_control_dashboard_path`.
|
|
41
|
+
5. Smoke test Solid Queue workers, Action Cable, and admin UI flows after the upgrade.
|
|
42
|
+
|
|
43
|
+
## Example Applications
|
|
44
|
+
- `examples/basic_host/template.rb` – Minimal host that seeds a Rails blog source and redirects `/` to the dashboard.
|
|
45
|
+
- `examples/advanced_host/template.rb` – Production-style integration with Mission Control, Redis realtime, Solid Queue tuning, and metrics endpoint.
|
|
46
|
+
- `examples/custom_adapter/template.rb` – Registers the sample Markdown scraper adapter and seeds a Markdown-based source.
|
|
47
|
+
- `examples/docker` – Dockerfile, Compose stack, and entrypoint script that run any generated example alongside Postgres and Redis.
|
|
48
|
+
|
|
49
|
+
See [examples/README.md](examples/README.md) for usage instructions.
|
|
50
|
+
|
|
51
|
+
## Architecture at a Glance
|
|
52
|
+
- **Source Lifecycle** – `SourceMonitor::Fetching::FetchRunner` coordinates advisory locking, fetch execution, retention pruning, and scrape enqueues. Source models store health metrics, failure states, and adaptive scheduling parameters.
|
|
53
|
+
- **Item Processing** – `SourceMonitor::Items::RetentionPruner`, `SourceMonitor::Scraping::Enqueuer`, and `SourceMonitor::Scraping::ItemScraper` keep content fresh, ensure deduplicated storage, and capture scrape metadata/logs.
|
|
54
|
+
- **Scraping Pipeline** – Adapters inherit from `SourceMonitor::Scrapers::Base`, merging default + source + invocation settings and returning structured results. The bundled Readability adapter composes `SourceMonitor::Scrapers::Fetchers::HttpFetcher` and `SourceMonitor::Scrapers::Parsers::ReadabilityParser`.
|
|
55
|
+
- **Realtime Dashboard** – `SourceMonitor::Dashboard::Queries` batches SQL, caches per-request responses, emits instrumentation (`source_monitor.dashboard.*`), and coordinates Turbo broadcasts via Solid Cable.
|
|
56
|
+
- **Observability** – `SourceMonitor::Metrics` tracks counters/gauges for fetches, scheduler runs, and dashboard activity. ActiveSupport notifications (`source_monitor.fetch.*`, `source_monitor.scheduler.run`, etc.) let you instrument external systems without monkey patches.
|
|
57
|
+
- **Extensibility** – `SourceMonitor.configure` exposes namespaces for queue tuning, HTTP defaults, scraper registry, retention, event callbacks, model extensions, authentication hooks, realtime transports, health thresholds, and job metrics.
|
|
58
|
+
|
|
59
|
+
## Admin Experience
|
|
60
|
+
- Dashboard cards summarising source counts, recent activity, queue visibility, and upcoming fetch schedules
|
|
61
|
+
- Source CRUD with scraping toggles, adaptive fetch controls, manual fetch triggers, and detailed fetch log timelines
|
|
62
|
+
- Item explorer showing feed vs scraped content, scrape status badges, and manual scrape actions via Turbo
|
|
63
|
+
- Fetch/scrape log viewers with HTTP status, duration, backtrace, and Solid Queue job references
|
|
64
|
+
|
|
65
|
+
## Background Jobs & Scheduling
|
|
66
|
+
- Solid Queue becomes the Active Job adapter when the host app still uses the inline `:async` adapter; queue names default to `source_monitor_fetch` and `source_monitor_scrape` and honour `ActiveJob.queue_name_prefix`.
|
|
67
|
+
- `config/recurring.yml` schedules minute-level fetches and scrapes. Run `bin/jobs --recurring_schedule_file=config/recurring.yml` (or set `SOLID_QUEUE_RECURRING_SCHEDULE_FILE`) to load recurring tasks. Disable with `SOLID_QUEUE_SKIP_RECURRING=true`.
|
|
68
|
+
- Retry/backoff behaviour is driven by `SourceMonitor.configure.fetching`. Fetch completion events and item processors allow you to chain downstream workflows (indexing, notifications, etc.).
|
|
69
|
+
|
|
70
|
+
## Configuration & API Surface
|
|
71
|
+
The generated initializer documents every setting. Key areas:
|
|
72
|
+
|
|
73
|
+
- Queue namespace/concurrency helpers (`SourceMonitor.queue_name(:fetch)`)
|
|
74
|
+
- HTTP, retry, and proxy settings (Faraday-backed)
|
|
75
|
+
- Scraper registry (`config.scrapers.register(:my_adapter, "MyApp::Scrapers::Custom")`)
|
|
76
|
+
- Retention defaults (`config.retention.items_retention_days`, `config.retention.strategy`)
|
|
77
|
+
- Lifecycle hooks (`config.events.after_item_created`, `config.events.register_item_processor`)
|
|
78
|
+
- Model extensions (table prefixes, included concerns, custom validations)
|
|
79
|
+
- Realtime adapter selection (`config.realtime.adapter = :solid_cable | :redis | :async`)
|
|
80
|
+
- Authentication helpers (`config.authentication.authenticate_with`, `authorize_with`, etc.)
|
|
81
|
+
- Mission Control toggles (`config.mission_control_enabled`, `config.mission_control_dashboard_path`)
|
|
82
|
+
- Health thresholds driving automatic pause/resume
|
|
83
|
+
|
|
84
|
+
See [docs/configuration.md](docs/configuration.md) for exhaustive coverage and examples.
|
|
85
|
+
|
|
86
|
+
## Deployment Considerations
|
|
87
|
+
- Copy engine migrations before every deploy and run `bin/rails db:migrate`.
|
|
88
|
+
- Precompile assets so SourceMonitor's bundled CSS/JS outputs are available at runtime.
|
|
89
|
+
- Run dedicated Solid Queue worker processes; consider a separate scheduler process for recurring jobs.
|
|
90
|
+
- Configure Action Cable (Solid Cable by default) and expose `/cable` through your load balancer.
|
|
91
|
+
- Monitor gauges/counters emitted by `SourceMonitor::Metrics` and subscribe to notifications for alerting.
|
|
92
|
+
|
|
93
|
+
More production guidance, including process topology and scaling tips, is available in [docs/deployment.md](docs/deployment.md).
|
|
94
|
+
|
|
95
|
+
## Troubleshooting & Support
|
|
96
|
+
Common installation and runtime issues (missing migrations, realtime not streaming, scraping failures, queue visibility gaps) are documented in [docs/troubleshooting.md](docs/troubleshooting.md). When you report bugs, include your `SourceMonitor::VERSION`, Rails version, configuration snippet, and relevant fetch/scrape logs so we can reproduce quickly.
|
|
97
|
+
|
|
98
|
+
## Development & Testing (Engine Repository)
|
|
99
|
+
- Install dependencies with `bundle install` and `npm install` (prefix with `rbenv exec` if using rbenv).
|
|
100
|
+
- Use `test/dummy/bin/dev` to boot the dummy app with npm CSS/JS watchers, Solid Queue worker, and Rails server.
|
|
101
|
+
- Run tests via `bin/test-coverage` (SimpleCov-enforced), `bundle exec rake app:test:smoke` for the fast subset, or `bin/rails test` for targeted suites.
|
|
102
|
+
- Quality checks: `bin/rubocop`, `bin/brakeman --no-pager`, `bin/lint-assets`.
|
|
103
|
+
- Record HTTP fixtures with VCR under `test/vcr_cassettes/` and keep coverage ≥ 90% for new code.
|
|
104
|
+
|
|
105
|
+
Contributions follow the clean architecture and TDD guidelines in `.ai/project_overview.md`. Review `.ai/tasks.md` to align with the active roadmap slice before opening a pull request.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
SourceMonitor is released under the [MIT License](MIT-LICENSE).
|
data/Rakefile
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Application } from "@hotwired/stimulus";
|
|
2
|
+
import AsyncSubmitController from "./controllers/async_submit_controller";
|
|
3
|
+
import NotificationController from "./controllers/notification_controller";
|
|
4
|
+
import DropdownController from "./controllers/dropdown_controller";
|
|
5
|
+
import ModalController from "./controllers/modal_controller";
|
|
6
|
+
import "./turbo_actions";
|
|
7
|
+
|
|
8
|
+
const existingApplication = window.SourceMonitorStimulus;
|
|
9
|
+
const application = existingApplication || Application.start();
|
|
10
|
+
|
|
11
|
+
if (!existingApplication) {
|
|
12
|
+
window.SourceMonitorStimulus = application;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
application.register("notification", NotificationController);
|
|
16
|
+
application.register("async-submit", AsyncSubmitController);
|
|
17
|
+
application.register("dropdown", DropdownController);
|
|
18
|
+
application.register("modal", ModalController);
|
|
19
|
+
|
|
20
|
+
export default application;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["button"];
|
|
5
|
+
static values = { loadingText: String };
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
if (this.hasButtonTarget) {
|
|
9
|
+
this.defaultText = this.buttonTarget.textContent;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
start() {
|
|
14
|
+
if (!this.hasButtonTarget) return;
|
|
15
|
+
|
|
16
|
+
const button = this.buttonTarget;
|
|
17
|
+
button.disabled = true;
|
|
18
|
+
button.dataset.originalText = this.defaultText || button.textContent;
|
|
19
|
+
if (this.hasLoadingTextValue && this.loadingTextValue) {
|
|
20
|
+
button.textContent = this.loadingTextValue;
|
|
21
|
+
}
|
|
22
|
+
button.classList.add("opacity-75", "pointer-events-none");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
finish() {
|
|
26
|
+
if (!this.hasButtonTarget) return;
|
|
27
|
+
|
|
28
|
+
const button = this.buttonTarget;
|
|
29
|
+
button.disabled = false;
|
|
30
|
+
button.classList.remove("opacity-75", "pointer-events-none");
|
|
31
|
+
const original = button.dataset.originalText;
|
|
32
|
+
if (original) {
|
|
33
|
+
button.textContent = original;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|