Skip to content

Commit f23c8e8

Browse files
committed
add api to return available request strategies
1 parent 0391fa5 commit f23c8e8

File tree

3 files changed

+91
-34
lines changed

3 files changed

+91
-34
lines changed

app.rb

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
require 'roda'
44
require 'rack/cache'
5+
require 'json'
56

67
require 'html2rss'
78
require_relative 'app/ssrf_filter_strategy'
89
require_relative 'app/auto_source'
910
require_relative 'app/feeds'
11+
require_relative 'app/health_check'
12+
require_relative 'app/api_routes'
1013

1114
module Html2rss
1215
module Web
@@ -15,6 +18,8 @@ module Web
1518
#
1619
# It is built with [Roda](https://roda.jeremyevans.net/).
1720
class App < Roda
21+
include ApiRoutes
22+
1823
CONTENT_TYPE_RSS = 'application/xml'
1924

2025
Html2rss::RequestService.register_strategy(:ssrf_filter, SsrfFilterStrategy)
@@ -71,8 +76,14 @@ def self.development? = ENV['RACK_ENV'] == 'development'
7176
JSON.generate(Feeds.list_feeds)
7277
end
7378

79+
r.on 'strategies.json' do
80+
response['Content-Type'] = 'application/json'
81+
response['Cache-Control'] = 'public, max-age=3600'
82+
JSON.generate(ApiRoutes.list_available_strategies)
83+
end
84+
7485
r.on String do |feed_name|
75-
handle_feed_generation(r, feed_name)
86+
ApiRoutes.handle_feed_generation(r, feed_name)
7687
end
7788
end
7889

@@ -98,25 +109,6 @@ def self.development? = ENV['RACK_ENV'] == 'development'
98109

99110
private
100111

101-
# API route helpers
102-
def handle_feed_generation(router, feed_name)
103-
params = router.params
104-
rss_content = Feeds.generate_feed(feed_name, params)
105-
set_rss_headers
106-
rss_content.to_s
107-
rescue StandardError => error
108-
response.status = 500
109-
response['Content-Type'] = CONTENT_TYPE_RSS
110-
Feeds.error_feed(error.message)
111-
end
112-
113-
def set_rss_headers
114-
response['Content-Type'] = CONTENT_TYPE_RSS
115-
response['Cache-Control'] = 'public, max-age=3600'
116-
response['X-Content-Type-Options'] = 'nosniff'
117-
response['X-XSS-Protection'] = '1; mode=block'
118-
end
119-
120112
# Auto source route helpers
121113
def auto_source_disabled_response
122114
response.status = 400

app/api_routes.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
module Html2rss
4+
module Web
5+
##
6+
# API routes for the html2rss-web application
7+
module ApiRoutes
8+
module_function
9+
10+
def list_available_strategies
11+
strategies = Html2rss::RequestService.strategy_names.map do |name|
12+
{
13+
name: name.to_s,
14+
display_name: name.to_s.split('_').map(&:capitalize).join(' ')
15+
}
16+
end
17+
18+
{ strategies: strategies }
19+
end
20+
21+
def handle_feed_generation(router, feed_name)
22+
params = router.params
23+
rss_content = Feeds.generate_feed(feed_name, params)
24+
set_rss_headers(router)
25+
rss_content.to_s
26+
rescue StandardError => error
27+
router.response.status = 500
28+
router.response['Content-Type'] = 'application/xml'
29+
Feeds.error_feed(error.message)
30+
end
31+
32+
def set_rss_headers(router)
33+
router.response['Content-Type'] = 'application/xml'
34+
router.response['Cache-Control'] = 'public, max-age=3600'
35+
router.response['X-Content-Type-Options'] = 'nosniff'
36+
router.response['X-XSS-Protection'] = '1; mode=block'
37+
end
38+
end
39+
end
40+
end

frontend/src/pages/index.astro

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,24 @@ import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
44
import { Icon, Card, Steps, Aside } from "@astrojs/starlight/components";
55
---
66

7-
<StarlightPage frontmatter={{
8-
title: "html2rss-web - Convert websites to RSS feeds",
9-
template: "splash",
10-
}}>
7+
<StarlightPage
8+
frontmatter={{
9+
title: "html2rss-web - Convert websites to RSS feeds",
10+
template: "splash",
11+
}}
12+
>
1113
<Card title="Generate RSS Feed">
1214
<form id="auto-source-form" novalidate>
1315
<div>
1416
<label for="url">Website URL:</label>
15-
<input
16-
type="url"
17-
id="url"
18-
name="url"
19-
placeholder="https://example.com"
20-
required
21-
autocomplete="url"
22-
/>
17+
<input type="url" id="url" name="url" placeholder="https://example.com" required autocomplete="url" />
2318
</div>
2419

2520
<div>
2621
<label for="strategy">Strategy:</label>
2722
<select id="strategy" name="strategy">
23+
<!-- Fallback options for when JS is disabled -->
2824
<option value="ssrf_filter">SSRF Filter (Recommended)</option>
29-
<option value="faraday">Faraday</option>
3025
</select>
3126
</div>
3227

@@ -42,7 +37,8 @@ import { Icon, Card, Steps, Aside } from "@astrojs/starlight/components";
4237
id="subscribe-link"
4338
href="#"
4439
class="sl-link-button"
45-
aria-label="Subscribe to the generated RSS feed">Subscribe</a>
40+
aria-label="Subscribe to the generated RSS feed">Subscribe</a
41+
>
4642
</p>
4743
</div>
4844
</div>
@@ -96,6 +92,35 @@ import { Icon, Card, Steps, Aside } from "@astrojs/starlight/components";
9692
// Initialize bookmarklet on page load
9793
initBookmarklet();
9894

95+
// Load available strategies dynamically
96+
async function loadStrategies() {
97+
try {
98+
const response = await fetch("/api/strategies.json");
99+
if (!response.ok) return;
100+
101+
const data = await response.json();
102+
const strategySelect = document.getElementById("strategy") as HTMLSelectElement;
103+
if (!strategySelect) return;
104+
105+
// Clear existing options
106+
strategySelect.innerHTML = "";
107+
108+
// Add strategies from API
109+
data.strategies.forEach((strategy: any) => {
110+
const option = document.createElement("option");
111+
option.value = strategy.name;
112+
option.textContent = strategy.display_name;
113+
strategySelect.appendChild(option);
114+
});
115+
} catch (error) {
116+
console.warn("Failed to load strategies:", error);
117+
// Fallback options remain in HTML
118+
}
119+
}
120+
121+
// Load strategies on page load
122+
loadStrategies();
123+
99124
// Handle URL parameters from bookmarklet
100125
function handleUrlParams() {
101126
const params = new URLSearchParams(window.location.search);

0 commit comments

Comments
 (0)