diff --git a/Dockerfile b/Dockerfile index ef1ecc05..f57cd199 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,6 +57,8 @@ RUN apk add --no-cache \ --no-create-home \ --uid "$UID" "$USER" \ && mkdir -p /app \ + && mkdir -p /app/tmp/rack-cache-body \ + && mkdir -p /app/tmp/rack-cache-meta \ && chown "$USER":"$USER" -R /app WORKDIR /app diff --git a/Gemfile b/Gemfile index abd532de..282f86d2 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,9 @@ source 'https://rubygems.org' git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } -gem 'html2rss', '~> 0.14' +# gem 'html2rss', '~> 0.14' +gem 'html2rss', github: 'html2rss/html2rss' + gem 'html2rss-configs', github: 'html2rss/html2rss-configs' # Use these instead of the two above (uncomment them) when developing locally: diff --git a/Gemfile.lock b/Gemfile.lock index f64402f4..5df59a51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,28 @@ +GIT + remote: https://github.com/html2rss/html2rss + revision: 70540c7d2accdfab85fb52d3839b31a615ffe3b3 + specs: + html2rss (0.17.0) + addressable (~> 2.7) + dry-validation + faraday (> 2.0.1, < 3.0) + faraday-follow_redirects + kramdown + mime-types (> 3.0) + nokogiri (>= 1.10, < 2.0) + parallel + puppeteer-ruby + regexp_parser + reverse_markdown (~> 3.0) + rss + sanitize + thor + tzinfo + zeitwerk + GIT remote: https://github.com/html2rss/html2rss-configs - revision: 6aadc0cf35c10642a4f15022e3ee9902360d3171 + revision: 1c8a122a66f4d42336bde000aeb0d9a01d8a4c7e specs: html2rss-configs (0.2.0) html2rss @@ -10,20 +32,55 @@ GEM specs: addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - ast (2.4.2) + ast (2.4.3) base64 (0.2.0) bigdecimal (3.1.9) - byebug (11.1.3) + byebug (12.0.0) climate_control (1.2.0) concurrent-ruby (1.3.5) crack (1.0.0) bigdecimal rexml crass (1.0.6) - diff-lcs (1.6.0) + diff-lcs (1.6.2) docile (1.4.1) + dry-configurable (1.3.0) + dry-core (~> 1.1) + zeitwerk (~> 2.6) + dry-core (1.1.0) + concurrent-ruby (~> 1.0) + logger + zeitwerk (~> 2.6) + dry-inflector (1.2.0) + dry-initializer (3.2.0) + dry-logic (1.6.0) + bigdecimal + concurrent-ruby (~> 1.0) + dry-core (~> 1.1) + zeitwerk (~> 2.6) + dry-schema (1.14.1) + concurrent-ruby (~> 1.0) + dry-configurable (~> 1.0, >= 1.0.1) + dry-core (~> 1.1) + dry-initializer (~> 3.2) + dry-logic (~> 1.5) + dry-types (~> 1.8) + zeitwerk (~> 2.6) + dry-types (1.8.2) + bigdecimal (~> 3.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) + dry-validation (1.11.1) + concurrent-ruby (~> 1.0) + dry-core (~> 1.1) + dry-initializer (~> 3.2) + dry-schema (~> 1.14) + zeitwerk (~> 2.6) erubi (1.13.1) - faraday (2.12.2) + faraday (2.13.1) faraday-net_http (>= 2.0, < 3.5) json logger @@ -31,55 +88,42 @@ GEM faraday (>= 1, < 3) faraday-net_http (3.4.0) net-http (>= 0.5.0) - hashdiff (1.1.2) - html2rss (0.17.0) - addressable (~> 2.7) - faraday (> 2.0.1, < 3.0) - faraday-follow_redirects - kramdown - mime-types (> 3.0) - nokogiri (>= 1.10, < 2.0) - parallel - puppeteer-ruby - regexp_parser - reverse_markdown (~> 3.0) - rss - sanitize (~> 6.0) - thor - tzinfo - zeitwerk - json (2.10.2) + hashdiff (1.2.0) + json (2.12.2) kramdown (2.5.1) rexml (>= 3.3.9) - language_server-protocol (3.17.0.4) + language_server-protocol (3.17.0.5) lint_roller (1.1.0) - logger (1.6.6) - mime-types (3.6.0) + logger (1.7.0) + mime-types (3.7.0) logger - mime-types-data (~> 3.2015) - mime-types-data (3.2025.0304) - mini_portile2 (2.8.8) + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0527) net-http (0.6.0) uri nio4r (2.7.4) - nokogiri (1.18.8) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) nokogiri (1.18.8-aarch64-linux-gnu) racc (~> 1.4) + nokogiri (1.18.8-aarch64-linux-musl) + racc (~> 1.4) nokogiri (1.18.8-arm-linux-gnu) racc (~> 1.4) + nokogiri (1.18.8-arm-linux-musl) + racc (~> 1.4) nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) nokogiri (1.18.8-x86_64-darwin) racc (~> 1.4) nokogiri (1.18.8-x86_64-linux-gnu) racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-musl) + racc (~> 1.4) parallel (1.27.0) - parser (3.3.7.1) + parser (3.3.8.0) ast (~> 2.4.1) racc - public_suffix (6.0.1) + prism (1.4.0) + public_suffix (6.0.2) puma (6.6.0) nio4r (~> 2.0) puppeteer-ruby (0.45.6) @@ -87,7 +131,7 @@ GEM mime-types (>= 3.0) websocket-driver (>= 0.6.0) racc (1.8.1) - rack (3.1.14) + rack (3.1.15) rack-cache (1.17.0) rack (>= 0.4) rack-test (2.2.0) @@ -102,22 +146,22 @@ GEM rexml (3.4.1) roda (3.92.0) rack - rspec (3.13.0) + rspec (3.13.1) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.3) + rspec-core (3.13.4) rspec-support (~> 3.13.0) - rspec-expectations (3.13.3) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.2) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.2) + rspec-support (3.13.4) rss (0.3.1) rexml - rubocop (1.74.0) + rubocop (1.75.8) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -125,29 +169,30 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.38.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.39.0) - parser (>= 3.3.1.0) - rubocop-performance (1.24.0) + rubocop-ast (1.44.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-performance (1.25.0) lint_roller (~> 1.1) - rubocop (>= 1.72.1, < 2.0) + rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0) rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) - rubocop-rspec (3.5.0) + rubocop-rspec (3.6.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-thread_safety (0.7.2) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) - sanitize (6.1.3) + sanitize (7.0.0) crass (~> 1.0.2) - nokogiri (>= 1.12.0) - sentry-ruby (5.23.0) + nokogiri (>= 1.16.8) + sentry-ruby (5.24.0) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) simplecov (0.22.0) @@ -172,27 +217,29 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket-driver (0.7.7) + websocket-driver (0.8.0) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) yard (0.9.37) - zeitwerk (2.7.2) + zeitwerk (2.7.3) PLATFORMS - aarch64-linux - arm-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl arm64-darwin - x86-linux x86_64-darwin - x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES base64 byebug climate_control erubi - html2rss (~> 0.14) + html2rss! html2rss-configs! parallel puma @@ -219,46 +266,56 @@ DEPENDENCIES CHECKSUMS addressable (2.8.7) sha256=462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232 - ast (2.4.2) sha256=1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12 + ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 base64 (0.2.0) sha256=0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507 bigdecimal (3.1.9) sha256=2ffc742031521ad69c2dfc815a98e426a230a3d22aeac1995826a75dabfad8cc - byebug (11.1.3) sha256=2485944d2bb21283c593d562f9ae1019bf80002143cc3a255aaffd4e9cf4a35b + byebug (12.0.0) sha256=d4a150d291cca40b66ec9ca31f754e93fed8aa266a17335f71bb0afa7fca1a1e climate_control (1.2.0) sha256=36b21896193fa8c8536fa1cd843a07cf8ddbd03aaba43665e26c53ec1bd70aa5 concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6 crack (1.0.0) sha256=c83aefdb428cdc7b66c7f287e488c796f055c0839e6e545fec2c7047743c4a49 crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d - diff-lcs (1.6.0) sha256=a1e7f7b272962f8fc769358ad00001b87cdcf32ba349d6c70c6b544613d2da2e + diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e + dry-configurable (1.3.0) sha256=882d862858567fc1210d2549d4c090f34370fc1bb7c5c1933de3fe792e18afa8 + dry-core (1.1.0) sha256=0903821a9707649a7da545a2cd88e20f3a663ab1c5288abd7f914fa7751ab195 + dry-inflector (1.2.0) sha256=22f5d0b50fd57074ae57e2ca17e3b300e57564c218269dcf82ff3e42d3f38f2e + dry-initializer (3.2.0) sha256=37d59798f912dc0a1efe14a4db4a9306989007b302dcd5f25d0a2a20c166c4e3 + dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2 + dry-schema (1.14.1) sha256=2fcd7539a7099cacae6a22f6a3a2c1846fe5afeb1c841cde432c89c6cb9b9ff1 + dry-types (1.8.2) sha256=c84e9ada69419c727c3b12e191e0ed7d2c6d58d040d55e79ea16e0ebf8b3ec0f + dry-validation (1.11.1) sha256=70900bb5a2d911c8aab566d3e360c6bff389b8bf92ea8e04885ce51c41ff8085 erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9 - faraday (2.12.2) sha256=157339c25c7b8bcb739f5cf1207cb0cefe8fa1c65027266bcbc34c90c84b9ad6 + faraday (2.13.1) sha256=cc531eb5467e7d74d4517630fa96f1a7003647cbf20a9a3e067d098941217b75 faraday-follow_redirects (0.3.0) sha256=d92d975635e2c7fe525dd494fcd4b9bb7f0a4a0ec0d5f4c15c729530fdb807f9 faraday-net_http (3.4.0) sha256=a1f1e4cd6a2cf21599c8221595e27582d9936819977bbd4089a601f24c64e54a - hashdiff (1.1.2) sha256=2c30eeded6ed3dce8401d2b5b99e6963fe5f14ed85e60dd9e33c545a44b71a77 - html2rss (0.17.0) sha256=f97c978272212fb9b67dfe8f9712a212ff22c9a812f574e9d64df703a8278370 + hashdiff (1.2.0) sha256=c984f13e115bfc9953332e8e83bd9d769cfde9944e2d54e07eb9df7b76e140b5 + html2rss (0.17.0) html2rss-configs (0.2.0) - json (2.10.2) sha256=34e0eada93022b2a0a3345bb0b5efddb6e9ff5be7c48e409cfb54ff8a36a8b06 + json (2.12.2) sha256=ba94a48ad265605c8fa9a50a5892f3ba6a02661aa010f638211f3cb36f44abf4 kramdown (2.5.1) sha256=87bbb6abd9d3cebe4fc1f33e367c392b4500e6f8fa19dd61c0972cf4afe7368c - language_server-protocol (3.17.0.4) sha256=c484626478664fd13482d8180947c50a8590484b1258b99b7aedb3b69df89669 + language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 - logger (1.6.6) sha256=dd618d24e637715472732e7eed02e33cfbdf56deaad225edd0f1f89d38024017 - mime-types (3.6.0) sha256=6f71db957840ceae44211531eff3e2f7e0dd4645fefb5f535dbaeb6307ab6464 - mime-types-data (3.2025.0304) sha256=7ed9385881432d708b7f4e9cf2a4a9e1d4e8bf8abcd08283c23a0d5cd7205097 - mini_portile2 (2.8.8) sha256=8e47136cdac04ce81750bb6c09733b37895bf06962554e4b4056d78168d70a75 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56 + mime-types-data (3.2025.0527) sha256=11808d780cdb27ea5db0143dbfc261b91ed63ec5e595f424a35f9822cb497a02 net-http (0.6.0) sha256=9621b20c137898af9d890556848c93603716cab516dc2c89b01a38b894e259fb nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9 - nokogiri (1.18.8) sha256=8c7464875d9ca7f71080c24c0db7bcaa3940e8be3c6fc4bcebccf8b9a0016365 nokogiri (1.18.8-aarch64-linux-gnu) sha256=36badd2eb281fca6214a5188e24a34399b15d89730639a068d12931e2adc210e + nokogiri (1.18.8-aarch64-linux-musl) sha256=664e0f9a77a7122a66d6c03abba7641ca610769a4728db55ee1706a0838b78a2 nokogiri (1.18.8-arm-linux-gnu) sha256=17de01ca3adf9f8e187883ed73c672344d3dbb3c260f88ffa1008e8dc255a28e + nokogiri (1.18.8-arm-linux-musl) sha256=6e6d7e71fc39572bd613a82d528cf54392c3de1ba5ce974f05c832b8187a040b nokogiri (1.18.8-arm64-darwin) sha256=483b5b9fb33653f6f05cbe00d09ea315f268f0e707cfc809aa39b62993008212 nokogiri (1.18.8-x86_64-darwin) sha256=024cdfe7d9ae3466bba6c06f348fb2a8395d9426b66a3c82f1961b907945cc0c nokogiri (1.18.8-x86_64-linux-gnu) sha256=4a747875db873d18a2985ee2c320a6070c4a414ad629da625fbc58d1a20e5ecc + nokogiri (1.18.8-x86_64-linux-musl) sha256=ddd735fba49475a395b9ea793bb6474e3a3125b89960339604d08a5397de1165 parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 - parser (3.3.7.1) sha256=7dbe61618025519024ac72402a6677ead02099587a5538e84371b76659e6aca1 - public_suffix (6.0.1) sha256=61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f + parser (3.3.8.0) sha256=2476364142b307fa5a1b1ece44f260728be23858a9c71078e956131a75453c45 + prism (1.4.0) sha256=dc0e3e00e93160213dc2a65519d9002a4a1e7b962db57d444cf1a71565bb703e + public_suffix (6.0.2) sha256=bfa7cd5108066f8c9602e0d6d4114999a5df5839a63149d3e8b0f9c1d3558394 puma (6.6.0) sha256=f25c06873eb3d5de5f0a4ebc783acc81a4ccfe580c760cfe323497798018ad87 puppeteer-ruby (0.45.6) sha256=cb86f7b4f6f8658a709ae1a305e820bdb009548e6beff6675489926f9ceb5995 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f - rack (3.1.14) sha256=84613c2a8df193bb6711d9c14ecc6d5a65a7cb4312379a65e793562608944b44 + rack (3.1.15) sha256=d12b3e9960d18a26ded961250f2c0e3b375b49ff40dbe6786e9c3b160cbffca4 rack-cache (1.17.0) sha256=49592f3ef2173b0f5524df98bb801fb411e839869e7ce84ac428dc492bf0eb90 rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463 rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9 @@ -269,21 +326,21 @@ CHECKSUMS reverse_markdown (3.0.0) sha256=ab228386765a0259835873cd07054b62939c40f620c77c247eafaaa3b23faca4 rexml (3.4.1) sha256=c74527a9a0a04b4ec31dbe0dc4ed6004b960af943d8db42e539edde3a871abca roda (3.92.0) sha256=2da31fc792c00dd6927d336519a13bea29e1fc500821ccf60471c37b9732619e - rspec (3.13.0) sha256=d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993 - rspec-core (3.13.3) sha256=25136507f4f9cf2e8977a2851e64e438b4331646054e345998714108745cdfe4 - rspec-expectations (3.13.3) sha256=0e6b5af59b900147698ea0ff80456c4f2e69cac4394fbd392fbd1ca561f66c58 - rspec-mocks (3.13.2) sha256=2327335def0e1665325a9b617e3af9ae20272741d80ac550336309a7c59abdef - rspec-support (3.13.2) sha256=cea3a2463fd9b84b9dcc9685efd80ea701aa8f7b3decb3b3ce795ed67737dbec + rspec (3.13.1) sha256=b9f9a58fa915b8d94a1d6b3195fe6dd28c4c34836a6097015142c4a9ace72140 + rspec-core (3.13.4) sha256=f9da156b7b775c82610a7b580624df51a55102f8c8e4a103b98f5d7a9fa23958 + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-mocks (3.13.5) sha256=e4338a6f285ada9fe56f5893f5457783af8194f5d08884d17a87321d5195ea81 + rspec-support (3.13.4) sha256=184b1814f6a968102b57df631892c7f1990a91c9a3b9e80ef892a0fc2a71a3f7 rss (0.3.1) sha256=b46234c04551b925180f8bedfc6f6045bf2d9998417feda72f300e7980226737 - rubocop (1.74.0) sha256=06138a35d7d11c963d5abc0148b355e3999007cb0225a619940db0e75521379b - rubocop-ast (1.39.0) sha256=b6ba0f677ceced033b81c69405ac8931f4963116c572b8da5e15a03619a8236c - rubocop-performance (1.24.0) sha256=e5bd39ff3e368395b9af886927cc37f5892f43db4bd6c8526594352d5b4440b5 + rubocop (1.75.8) sha256=c80ab4286c5dcfc49d7ad1787cdba5569b63b58c96ee7afde4ec47a9c8a85be9 + rubocop-ast (1.44.1) sha256=e3cc04203b2ef04f6d6cf5f85fe6d643f442b18cc3b23e3ada0ce5b6521b8e92 + rubocop-performance (1.25.0) sha256=6f7d03568a770054117a78d0a8e191cefeffb703b382871ca7743831b1a52ec1 rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d - rubocop-rspec (3.5.0) sha256=710c942fe1af884ba8eea75cbb8bdbb051929a2208880a6fc2e2dce1eed5304c + rubocop-rspec (3.6.0) sha256=c0e4205871776727e54dee9cc91af5fd74578001551ba40e1fe1a1ab4b404479 rubocop-thread_safety (0.7.2) sha256=bd51449c420b1ddda5672b71a39706367402beb55aaf19fc020c1868717f31f6 ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 - sanitize (6.1.3) sha256=a42ce5f933d82765a8243599d5ab2e8867237e76cf5a699caaddfe60bb944152 - sentry-ruby (5.23.0) sha256=8e8bb2f9a56a267a50fcba947f2ae131b6542f45fc3bb5764c2c25ba68f385cc + sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984 + sentry-ruby (5.24.0) sha256=420b15f4d81cf1cfddc4cb73d2282bcbaf111fe3554a6aa142f4cee2b870c437 simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5 simplecov-html (0.13.1) sha256=5dab0b7ee612e60e9887ad57693832fdf4695b4c0c859eaea5f95c18791ef10b simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428 @@ -297,10 +354,10 @@ CHECKSUMS uri (1.0.3) sha256=e9f2244608eea2f7bc357d954c65c910ce0399ca5e18a7a29207ac22d8767011 vcr (6.3.1) sha256=37b56e157e720446a3f4d2d39919cabef8cb7b6c45936acffd2ef8229fec03ed webmock (3.25.1) sha256=ab9d5d9353bcbe6322c83e1c60a7103988efc7b67cd72ffb9012629c3d396323 - websocket-driver (0.7.7) sha256=056d99f2cd545712cfb1291650fde7478e4f2661dc1db6a0fa3b966231a146b4 + websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962 websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241 yard (0.9.37) sha256=a6e910399e78e613f80ba9add9ba7c394b1a935f083cccbef82903a3d2a26992 - zeitwerk (2.7.2) sha256=842e067cb11eb923d747249badfb5fcdc9652d6f20a1f06453317920fdcd4673 + zeitwerk (2.7.3) sha256=b2e86b4a9b57d26ba68a15230dcc7fe6f040f06831ce64417b0621ad96ba3e85 BUNDLED WITH 2.6.6 diff --git a/app.rb b/app.rb index a1c30b3a..e6e84e8d 100644 --- a/app.rb +++ b/app.rb @@ -84,11 +84,38 @@ def self.development? = ENV['RACK_ENV'] == 'development' end r.on String, String do |folder_name, config_name_with_ext| - handle_html2rss_configs(request, folder_name, config_name_with_ext) + response['Content-Type'] = CONTENT_TYPE_RSS + + name = "#{folder_name}/#{File.basename(config_name_with_ext, '.*')}" + config = Html2rss::Configs.find_by_name(name) + + if (params = request.params).any? + config[:params] ||= {} + config[:params].merge!(params) + end + + feed = Html2rss.feed(config) + + HttpCache.expires(response, feed.channel.ttl.to_i * 60, cache_control: 'public') + + feed.to_s end r.on String do |config_name_with_ext| - handle_local_config_feeds(request, config_name_with_ext) + response['Content-Type'] = CONTENT_TYPE_RSS + + config = LocalConfig.find(File.basename(config_name_with_ext, '.*')) + + if (params = request.params).any? + config[:params] ||= {} + config[:params].merge!(params) + end + + feed = Html2rss.feed(config) + + HttpCache.expires(response, feed.channel.ttl.to_i * 60, cache_control: 'public') + + feed.to_s end end diff --git a/app/html2rss_facade.rb b/app/html2rss_facade.rb deleted file mode 100644 index 4951860e..00000000 --- a/app/html2rss_facade.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true - -require 'html2rss' -require 'html2rss/configs' -require_relative 'local_config' - -module Html2rss - module Web - ## - # Provides methods to work with html2rss and html2rss-configs without - # knowing much of their interface. - class Html2rssFacade - private_class_method :new - - attr_reader :feed_config, :typecast_params - - ## - # @param name [String] the name of a html2rss-configs provided config. - # @param typecast_params [Object] - # @return [String] the serialized RSS feed - def self.from_config(name, typecast_params, &) - feed_config = Html2rss::Configs.find_by_name(name) - new(feed_config, typecast_params).feed(&) - end - - ## - # @param name [String] the name of a feed in the file `config/feeds.yml` - # @param typecast_params [Object] - # @return [String] the serialized RSS feed - def self.from_local_config(name, typecast_params, &) - feed_config = LocalConfig.find(name) - new(feed_config, typecast_params).feed(&) - end - - ## - # @param feed_config [Hash] - # @param typecast_params [Object] - # @param global_config [Hash] - # @return [Html2rss::Config] - # @raise [Roda::RodaPlugins::TypecastParams::Error] - def self.feed_config_to_config(feed_config, typecast_params, global_config: LocalConfig.global) - dynamic_params = Html2rss::Config::Channel.required_params_for_config(feed_config[:channel]) - .to_h { |name| [name, typecast_params.str!(name)] } - Html2rss::Config.new(feed_config, global_config, dynamic_params) - end - - ## - # @param feed_config [Hash] - # @param typecast_params [Object] - def initialize(feed_config, typecast_params) - @feed_config = feed_config - @typecast_params = typecast_params - end - - ## - # @return [String] - def feed - config = self.class.feed_config_to_config(feed_config, typecast_params) - yield config if block_given? - Html2rss.feed(config).to_s - end - end - end -end diff --git a/app/http_cache.rb b/app/http_cache.rb index 1e17920a..8b86fadb 100644 --- a/app/http_cache.rb +++ b/app/http_cache.rb @@ -15,6 +15,8 @@ module HttpCache # @param seconds [Integer] # @param cache_control [String, nil] def expires(response, seconds, cache_control: nil) + expires_now(response) and return if seconds <= 0 + response['Expires'] = (Time.now + seconds).httpdate cache_value = "max-age=#{seconds}" diff --git a/app/request_path.rb b/app/request_path.rb deleted file mode 100644 index f1faf1fb..00000000 --- a/app/request_path.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module Html2rss - module Web - ## - # Provides helper methods to get config names by the request path. - class RequestPath - attr_reader :folder_name - - ## - # @param request [Rack::Request, #path] - def initialize(request) - @full_path = request.path[1..] - parts = @full_path.split('/') - - if parts.size == 1 - @name_with_ext = @full_path - else - @folder_name = parts[0..-2] - @name_with_ext = parts[-1] - end - end - - ## - # @return [String] - def full_config_name - [@folder_name, config_name].compact.join('/') - end - - ## - # @return [String] - def config_name - parts[..-2].join('.') - end - - ## - # @return [String] - def extension - parts.last - end - - private - - def parts - @parts ||= @name_with_ext.split('.') - end - end - end -end diff --git a/app/ssrf_filter_strategy.rb b/app/ssrf_filter_strategy.rb index 48b7541e..a6bb9758 100644 --- a/app/ssrf_filter_strategy.rb +++ b/app/ssrf_filter_strategy.rb @@ -16,6 +16,7 @@ def execute response = SsrfFilter.get(ctx.url, headers:) Html2rss::RequestService::Response.new(body: response.body, + url: ctx.url, headers: response.to_hash.transform_values(&:first)) end end diff --git a/config/feeds.yml b/config/feeds.yml index c8126682..503e9631 100644 --- a/config/feeds.yml +++ b/config/feeds.yml @@ -16,6 +16,6 @@ feeds: selector: "li > div" title: selector: "h4" - link: + url: selector: "a" extractor: "href" diff --git a/helpers/handle_error.rb b/helpers/handle_error.rb index b3245c0e..b5d9b9ee 100644 --- a/helpers/handle_error.rb +++ b/helpers/handle_error.rb @@ -8,12 +8,10 @@ module Web class App def handle_error(error) # rubocop:disable Metrics/MethodLength case error - when Html2rss::Config::ParamsMissing, + when Html2rss::Config::DynamicParams::ParamsMissing, Roda::RodaPlugins::TypecastParams::Error set_error_response('Parameters missing or invalid', 422) - when Html2rss::AttributePostProcessors::UnknownPostProcessorName, - Html2rss::ItemExtractors::UnknownExtractorName, - Html2rss::Config::ChannelMissing + when Html2rss::Selectors::PostProcessors::UnknownPostProcessorName set_error_response('Invalid feed config', 422) when LocalConfig::NotFound, Html2rss::Configs::ConfigNotFound diff --git a/helpers/handle_html2rss_configs.rb b/helpers/handle_html2rss_configs.rb deleted file mode 100644 index de616276..00000000 --- a/helpers/handle_html2rss_configs.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Html2rss - module Web - class App - def handle_html2rss_configs(request, _folder_name, _config_name_with_ext) - path = RequestPath.new(request) - - Html2rssFacade.from_config(path.full_config_name, typecast_params) do |config| - response['Content-Type'] = CONTENT_TYPE_RSS - HttpCache.expires(response, config.ttl * 60, cache_control: 'public') - end - end - end - end -end diff --git a/helpers/handle_local_config_feeds.rb b/helpers/handle_local_config_feeds.rb deleted file mode 100644 index 533fabf1..00000000 --- a/helpers/handle_local_config_feeds.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Html2rss - module Web - class App - def handle_local_config_feeds(request, _config_name_with_ext) - path = RequestPath.new(request) - - Html2rssFacade.from_local_config(path.full_config_name, typecast_params) do |config| - response['Content-Type'] = CONTENT_TYPE_RSS - HttpCache.expires(response, config.ttl * 60, cache_control: 'public') - end - end - end - end -end diff --git a/routes/auto_source.rb b/routes/auto_source.rb index 345ac5f4..81c8167b 100644 --- a/routes/auto_source.rb +++ b/routes/auto_source.rb @@ -21,28 +21,19 @@ class App r.on String, method: :get do |encoded_url| strategy = (request.params['strategy'] || :ssrf_filter).to_sym - unless Html2rss::RequestService.strategy_registered?(strategy) - raise Html2rss::RequestService::UnknownStrategy - end - response['Content-Type'] = CONTENT_TYPE_RSS - - url = Addressable::URI.parse Base64.urlsafe_decode64(encoded_url) - rss = Html2rss.auto_source(url, strategy:) + url = Addressable::URI.parse(Base64.urlsafe_decode64(encoded_url)) - # Unfortunately, Ruby's rss gem does not provide a direct method to - # add an XML stylesheet to the RSS::RSS object itself. - stylesheet = Html2rss::RssBuilder::Stylesheet.new(href: '/rss.xsl', type: 'text/xsl').to_xml + feed = Html2rss.feed(stylesheets: [{ href: '/rss.xsl', type: 'text/xsl' }], + strategy:, + channel: { url: url.to_s }, + auto_source: {}) - xml_content = rss.to_xml - xml_content.sub!(/^<\?xml version="1.0" encoding="UTF-8"\?>/, - "\n#{stylesheet}") + HttpCache.expires(response, AutoSource.ttl_in_seconds(feed), cache_control: 'private, must-revalidate') - HttpCache.expires response, - AutoSource.ttl_in_seconds(rss), - cache_control: 'private, must-revalidate' - - xml_content + response['Content-Type'] = CONTENT_TYPE_RSS + response.status = 200 + feed.to_xml end else # auto_source feature is disabled diff --git a/spec/html2rss/web/app/html2rss_facade_spec.rb b/spec/html2rss/web/app/html2rss_facade_spec.rb deleted file mode 100644 index 4156874f..00000000 --- a/spec/html2rss/web/app/html2rss_facade_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../app/html2rss_facade' - -RSpec.describe Html2rss::Web::Html2rssFacade do - describe '.from_config(name, typecast_params, &block)' do - let(:typecast_params) do - double({}, str!: nil) # rubocop:disable RSpec/VerifiedDoubles - end - - let(:name) { Html2rss::Configs.file_names.first.split('/')[-2..].join('/').split('.')[0..-2].join('.') } - - before do - allow(typecast_params).to receive(:str!) - allow(Html2rss).to receive(:feed) - end - - context 'without dynamic params' do - it do - described_class.from_config(name, typecast_params) - expect(typecast_params).not_to have_received(:str!) - end - end - - it 'yields a Html2rss::Config' do - expect { |b| described_class.from_config(name, typecast_params, &b) }.to yield_with_args - end - end -end diff --git a/spec/html2rss/web/app/request_path_spec.rb b/spec/html2rss/web/app/request_path_spec.rb deleted file mode 100644 index be644aef..00000000 --- a/spec/html2rss/web/app/request_path_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'rack/request' -require_relative '../../../../app/request_path' - -RSpec.describe Html2rss::Web::RequestPath do - subject(:instance) { described_class.new(request) } - - context 'with a local config' do - let(:request) { instance_double(Rack::Request, path: '/example.rss') } - - describe '#full_config_name' do - it { expect(instance.full_config_name).to eq 'example' } - end - - describe '#config_name' do - it { expect(instance.config_name).to eq 'example' } - end - - describe '#extension' do - it { expect(instance.extension).to eq 'rss' } - end - end - - context 'with a html2rss-config' do - let(:request) { instance_double(Rack::Request, path: '/github.com/releases.rss') } - - describe '#full_config_name' do - it { expect(instance.full_config_name).to eq 'github.com/releases' } - end - - describe '#config_name' do - it { expect(instance.config_name).to eq 'releases' } - end - - describe '#extension' do - it { expect(instance.extension).to eq 'rss' } - end - end -end diff --git a/spec/routes/auto_source_spec.rb b/spec/routes/auto_source_spec.rb index ab7a5fbd..0dd2b737 100644 --- a/spec/routes/auto_source_spec.rb +++ b/spec/routes/auto_source_spec.rb @@ -76,8 +76,9 @@ def app = described_class it 'responds successfully', :aggregate_failures do expect(response).to be_ok + expect(response.status).to eq 200 expect(response.body).to start_with '' - expect(response.get_header('cache-control')).to eq 'must-revalidate, private, max-age=0' + expect(response.get_header('cache-control')).to eq 'must-revalidate, no-cache, no-store, private, max-age=0' expect(response.get_header('content-type')).to eq described_class::CONTENT_TYPE_RSS end end @@ -93,7 +94,7 @@ def app = described_class it 'responds with Error', :aggregate_failures do expect(response.status).to eq 422 - expect(response.body).to match(/UnknownStrategy/) + expect(response.body).to match(/Html2rss::Config::InvalidConfig/) end end end