Skip to content

Commit 29c825c

Browse files
committed
refactor: enhance routing and response handling; improve frontend visibility toggles
1 parent 5b097d3 commit 29c825c

File tree

14 files changed

+175
-143
lines changed

14 files changed

+175
-143
lines changed

app.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def self.production_error_message
7272
ERROR
7373
end
7474

75+
def development? = self.class.development?
76+
7577
# Validate environment on class load
7678
validate_environment!
7779

@@ -146,7 +148,7 @@ def self.production_error_message
146148
# Stable feed routes (new)
147149
hash_branch 'feeds' do |r|
148150
r.on String do |feed_id|
149-
handle_stable_feed(r, feed_id)
151+
AutoSourceRoutes.handle_stable_feed(r, feed_id)
150152
end
151153
end
152154

app/api_routes.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require_relative 'local_config'
4+
35
module Html2rss
46
module Web
57
##
@@ -21,17 +23,22 @@ def list_available_strategies
2123
def handle_feed_generation(router, feed_name)
2224
params = router.params
2325
rss_content = Feeds.generate_feed(feed_name, params)
24-
rss_headers(router)
26+
27+
# Extract TTL from feed configuration
28+
config = LocalConfig.find(feed_name)
29+
ttl = config.dig(:channel, :ttl) || 3600
30+
31+
rss_headers(router, ttl: ttl)
2532
rss_content.to_s
2633
rescue StandardError => error
2734
router.response.status = 500
2835
router.response['Content-Type'] = 'application/xml'
2936
Feeds.error_feed(error.message)
3037
end
3138

32-
def rss_headers(router)
39+
def rss_headers(router, ttl: 3600)
3340
router.response['Content-Type'] = 'application/xml'
34-
router.response['Cache-Control'] = 'public, max-age=3600'
41+
router.response['Cache-Control'] = "public, max-age=#{ttl}"
3542
end
3643
end
3744
end

app/auto_source.rb

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,6 @@ def generate_feed_from_stable_id(feed_id, token_data)
7272
}
7373
end
7474

75-
private
76-
77-
def build_feed_data(name, url, token_data, strategy, feed_id, feed_token)
78-
public_url = "/feeds/#{feed_id}?token=#{feed_token}&url=#{URI.encode_www_form_component(url)}"
79-
80-
{
81-
id: feed_id,
82-
name: name,
83-
url: url,
84-
username: token_data[:username],
85-
strategy: strategy,
86-
public_url: public_url
87-
}
88-
end
89-
9075
def generate_feed_content(url, strategy = 'ssrf_filter')
9176
feed_content = call_strategy(url, strategy)
9277

@@ -102,6 +87,19 @@ def generate_feed_content(url, strategy = 'ssrf_filter')
10287
feed_content
10388
end
10489

90+
def build_feed_data(name, url, token_data, strategy, feed_id, feed_token)
91+
public_url = "/feeds/#{feed_id}?token=#{feed_token}&url=#{URI.encode_www_form_component(url)}"
92+
93+
{
94+
id: feed_id,
95+
name: name,
96+
url: url,
97+
username: token_data[:username],
98+
strategy: strategy,
99+
public_url: public_url
100+
}
101+
end
102+
105103
def create_empty_feed_warning(url, strategy)
106104
site_title = extract_site_title(url)
107105
XmlBuilder.build_empty_feed_warning(
@@ -111,7 +109,7 @@ def create_empty_feed_warning(url, strategy)
111109
)
112110
end
113111

114-
def call_strategy(url, strategy)
112+
def call_strategy(url, strategy) # rubocop:disable Metrics/MethodLength
115113
global_config = LocalConfig.global
116114

117115
config = {

app/auto_source_routes.rb

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,17 @@ def handle_stable_feed(router, feed_id)
5353
handle_auto_source_error(router, error)
5454
end
5555

56-
private
56+
def handle_public_feed_access(router, _feed_id, feed_token, url)
57+
# Validate feed token and URL
58+
return access_denied_response(router, url) unless Auth.feed_url_allowed?(feed_token, url)
5759

58-
def auto_source_disabled_response(router)
59-
router.response.status = 400
60-
router.response['Content-Type'] = 'application/xml'
61-
XmlBuilder.build_error_feed(message: 'The auto source feature is disabled.', title: 'Auto Source Disabled')
60+
strategy = router.params['strategy'] || 'ssrf_filter'
61+
rss_content = AutoSource.generate_feed_content(url, strategy)
62+
63+
configure_auto_source_headers(router)
64+
rss_content.to_s
65+
rescue StandardError => error
66+
handle_auto_source_error(router, error)
6267
end
6368

6469
def handle_authenticated_feed_access(router, url)
@@ -70,21 +75,72 @@ def handle_authenticated_feed_access(router, url)
7075
strategy = router.params['strategy'] || 'ssrf_filter'
7176
rss_content = AutoSource.generate_feed_content(url, strategy)
7277

73-
set_auto_source_headers(router)
78+
configure_auto_source_headers(router)
7479
rss_content.to_s
7580
end
7681

77-
def handle_public_feed_access(router, _feed_id, feed_token, url)
78-
# Validate feed token and URL
79-
return access_denied_response(router, url) unless Auth.feed_url_allowed?(feed_token, url)
82+
def handle_auto_source_error(router, error)
83+
router.response.status = 500
84+
router.response['Content-Type'] = 'application/xml'
85+
XmlBuilder.build_error_feed(message: error.message)
86+
end
8087

81-
strategy = router.params['strategy'] || 'ssrf_filter'
82-
rss_content = AutoSource.generate_feed_content(url, strategy)
88+
# Helper methods that need to be implemented by the main app
89+
def bad_request_response(router, message)
90+
router.response.status = 400
91+
router.response['Content-Type'] = 'application/xml'
92+
XmlBuilder.build_access_denied_feed(message)
93+
end
8394

84-
set_auto_source_headers(router)
85-
rss_content.to_s
86-
rescue StandardError => error
87-
handle_auto_source_error(router, error)
95+
def unauthorized_response(router)
96+
router.response.status = 401
97+
router.response['Content-Type'] = 'application/xml'
98+
XmlBuilder.build_error_feed(message: 'Unauthorized')
99+
end
100+
101+
def access_denied_response(router, url)
102+
router.response.status = 403
103+
router.response['Content-Type'] = 'application/xml'
104+
XmlBuilder.build_access_denied_feed(url)
105+
end
106+
107+
def method_not_allowed_response(router)
108+
router.response.status = 405
109+
router.response['Content-Type'] = 'application/xml'
110+
XmlBuilder.build_error_feed(message: 'Method Not Allowed')
111+
end
112+
113+
def internal_error_response(router)
114+
router.response.status = 500
115+
router.response['Content-Type'] = 'application/xml'
116+
XmlBuilder.build_error_feed(message: 'Internal Server Error')
117+
end
118+
119+
def forbidden_origin_response(router)
120+
router.response.status = 403
121+
router.response['Content-Type'] = 'application/xml'
122+
XmlBuilder.build_error_feed(message: 'Forbidden Origin')
123+
end
124+
125+
def configure_auto_source_headers(router)
126+
router.response['Content-Type'] = 'application/xml'
127+
router.response['Cache-Control'] = 'public, max-age=3600'
128+
router.response['X-Content-Type-Options'] = 'nosniff'
129+
router.response['X-XSS-Protection'] = '1; mode=block'
130+
end
131+
132+
def validate_and_decode_base64(encoded_url)
133+
Base64.urlsafe_decode64(encoded_url)
134+
rescue ArgumentError
135+
nil
136+
end
137+
138+
private
139+
140+
def auto_source_disabled_response(router)
141+
router.response.status = 400
142+
router.response['Content-Type'] = 'application/xml'
143+
XmlBuilder.build_error_feed(message: 'The auto source feature is disabled.', title: 'Auto Source Disabled')
88144
end
89145

90146
def handle_create_feed(router)
@@ -143,65 +199,9 @@ def process_legacy_auto_source_request(router, encoded_url, token_data)
143199

144200
strategy = router.params['strategy'] || 'ssrf_filter'
145201
rss_content = AutoSource.generate_feed(encoded_url, strategy)
146-
set_auto_source_headers(router)
202+
configure_auto_source_headers(router)
147203
rss_content.to_s
148204
end
149-
150-
def handle_auto_source_error(router, error)
151-
router.response.status = 500
152-
router.response['Content-Type'] = 'application/xml'
153-
XmlBuilder.build_error_feed(message: error.message)
154-
end
155-
156-
# Helper methods that need to be implemented by the main app
157-
def bad_request_response(router, message)
158-
router.response.status = 400
159-
router.response['Content-Type'] = 'application/xml'
160-
XmlBuilder.build_access_denied_feed(message)
161-
end
162-
163-
def unauthorized_response(router)
164-
router.response.status = 401
165-
router.response['Content-Type'] = 'application/xml'
166-
XmlBuilder.build_error_feed(message: 'Unauthorized')
167-
end
168-
169-
def access_denied_response(router, url)
170-
router.response.status = 403
171-
router.response['Content-Type'] = 'application/xml'
172-
XmlBuilder.build_access_denied_feed(url)
173-
end
174-
175-
def method_not_allowed_response(router)
176-
router.response.status = 405
177-
router.response['Content-Type'] = 'application/xml'
178-
XmlBuilder.build_error_feed(message: 'Method Not Allowed')
179-
end
180-
181-
def internal_error_response(router)
182-
router.response.status = 500
183-
router.response['Content-Type'] = 'application/xml'
184-
XmlBuilder.build_error_feed(message: 'Internal Server Error')
185-
end
186-
187-
def forbidden_origin_response(router)
188-
router.response.status = 403
189-
router.response['Content-Type'] = 'application/xml'
190-
XmlBuilder.build_error_feed(message: 'Forbidden Origin')
191-
end
192-
193-
def set_auto_source_headers(router)
194-
router.response['Content-Type'] = 'application/xml'
195-
router.response['Cache-Control'] = 'public, max-age=3600'
196-
router.response['X-Content-Type-Options'] = 'nosniff'
197-
router.response['X-XSS-Protection'] = '1; mode=block'
198-
end
199-
200-
def validate_and_decode_base64(encoded_url)
201-
Base64.urlsafe_decode64(encoded_url)
202-
rescue ArgumentError
203-
nil
204-
end
205205
end
206206
end
207207
end

app/response_helpers.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def internal_error_response
5252
XmlBuilder.build_error_feed(message: 'Internal Server Error')
5353
end
5454

55-
def set_auto_source_headers
55+
def configure_auto_source_headers
5656
response['Content-Type'] = 'application/xml'
5757
response['Cache-Control'] = 'private, must-revalidate, no-cache, no-store, max-age=0'
5858
response['X-Content-Type-Options'] = 'nosniff'

app/static_file_helpers.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ def handle_static_files(router)
1111
router.on do
1212
if router.path_info == '/'
1313
serve_root_path
14-
else
14+
elsif router.path_info.start_with?('/') && !router.path_info.include?('.')
15+
# Only handle frontend routes that don't have file extensions
1516
serve_astro_files(router)
1617
end
1718
end

frontend/src/components/FeedForm.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import "../styles/forms.css";
55

66
<div id="feed-section">
77
<!-- Advanced options toggle -->
8-
<button type="button" class="advanced-toggle" id="advanced-toggle"> Show advanced options </button>
8+
<button type="button" class="advanced-toggle" id="advanced-toggle">Show advanced options</button>
99

1010
<!-- Advanced fields (shown by default) -->
1111
<div class="advanced-fields" id="advanced-fields">

frontend/src/components/UrlInput.astro

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ import "../styles/forms.css";
2121
</div>
2222
<button type="submit" class="form-button">Convert</button>
2323
</div>
24-
<div class="quick-actions" id="quick-actions" style="display: none;">
24+
<div class="quick-actions hidden" id="quick-actions">
2525
<button type="button" class="form-button form-button-secondary" id="copy-url">Copy URL</button>
26-
<a href="#" class="form-button form-button-secondary" id="subscribe-link" style="text-decoration: none;"
27-
>Subscribe</a
28-
>
26+
<a href="#" class="form-button form-button-secondary subscribe-link" id="subscribe-link">Subscribe</a>
2927
</div>
3028
</div>
3129
</div>

frontend/src/components/XmlDisplay.astro

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import "../styles/forms.css";
44
---
55

66
<!-- Content Preview -->
7-
<div class="xml-preview" id="xml-preview" style="display: none;">
7+
<div class="xml-preview hidden" id="xml-preview">
88
<div class="loading">Loading RSS feed preview...</div>
99
</div>
1010

1111
<!-- XML Feed Display -->
12-
<div class="xml-feed-display" id="xml-feed-display" style="display: none;">
12+
<div class="xml-feed-display hidden" id="xml-feed-display">
1313
<div class="xml-feed-header">
1414
<h4>📄 RSS Feed Preview</h4>
1515
<div class="xml-feed-actions">
@@ -19,17 +19,10 @@ import "../styles/forms.css";
1919
<button type="button" class="xml-toggle" id="xml-toggle">Show Raw XML</button>
2020
</div>
2121
</div>
22-
<div class="xml-feed-content" id="xml-feed-content" style="display: none;">
23-
<iframe
24-
id="rss-iframe"
25-
src=""
26-
width="100%"
27-
height="800"
28-
frameborder="0"
29-
style="border: 1px solid var(--form-border); border-radius: var(--form-radius); min-height: 600px;"
30-
></iframe>
22+
<div class="xml-feed-content hidden" id="xml-feed-content">
23+
<iframe id="rss-iframe" src="" width="100%" height="800" frameborder="0" class="rss-iframe"></iframe>
3124
</div>
32-
<div class="xml-raw-content" id="xml-raw-content" style="display: none;">
25+
<div class="xml-raw-content hidden" id="xml-raw-content">
3326
<pre><code id="rss-content" /></pre>
3427
</div>
3528
</div>

0 commit comments

Comments
 (0)