1414require_relative 'app/response_helpers'
1515require_relative 'app/static_file_helpers'
1616require_relative 'app/xml_builder'
17+ require_relative 'app/auto_source_routes'
18+ require_relative 'app/health_check_routes'
1719
1820module Html2rss
1921 module Web
@@ -25,6 +27,8 @@ class App < Roda
2527 include ApiRoutes
2628 include ResponseHelpers
2729 include StaticFileHelpers
30+ include AutoSourceRoutes
31+ include HealthCheckRoutes
2832
2933 CONTENT_TYPE_RSS = 'application/xml'
3034
@@ -108,27 +112,28 @@ def self.production_error_message
108112
109113 plugin :exception_page
110114 plugin :error_handler do |error |
111- next exception_page ( error ) if ENV [ 'RACK_ENV' ] == ' development'
115+ next exception_page ( error ) if development?
112116
113117 response . status = 500
114- 'Internal Server Error'
118+ response [ 'Content-Type' ] = CONTENT_TYPE_RSS
119+ XmlBuilder . build_error_feed ( message : error . message )
115120 end
116121
117122 plugin :public
118123 plugin :hash_branches
119124
120- @show_backtrace = !ENV [ 'CI' ] . to_s . empty? || ( ENV [ 'RACK_ENV' ] == ' development' )
125+ @show_backtrace = !ENV [ 'CI' ] . to_s . empty? || development?
121126
122127 # API routes
123128 hash_branch 'api' do |r |
129+ response [ 'Content-Type' ] = 'application/json'
130+
124131 r . on 'feeds.json' do
125- response [ 'Content-Type' ] = 'application/json'
126132 response [ 'Cache-Control' ] = 'public, max-age=300'
127133 JSON . generate ( Feeds . list_feeds )
128134 end
129135
130136 r . on 'strategies.json' do
131- response [ 'Content-Type' ] = 'application/json'
132137 response [ 'Cache-Control' ] = 'public, max-age=3600'
133138 JSON . generate ( ApiRoutes . list_available_strategies )
134139 end
@@ -147,160 +152,19 @@ def self.production_error_message
147152
148153 # Auto source routes
149154 hash_branch 'auto_source' do |r |
150- return auto_source_disabled_response unless AutoSource . enabled?
151-
152- # New stable feed creation and management
153- r . on 'create' do
154- handle_create_feed ( r )
155- end
156-
157- r . on 'feeds' do
158- handle_list_feeds ( r )
159- end
160-
161- # Legacy encoded URL route (for backward compatibility)
162- r . on String do |encoded_url |
163- handle_legacy_auto_source_feed ( r , encoded_url )
164- end
155+ handle_auto_source_routes ( r )
165156 end
166157
167158 # Health check route
168159 hash_branch 'health_check.txt' do |r |
169- handle_health_check ( r )
160+ handle_health_check_routes ( r )
170161 end
171162
172163 route do |r |
173164 r . public
174165 r . hash_branches
175166 handle_static_files ( r )
176167 end
177-
178- private
179-
180- # Auto source route helpers
181- def auto_source_disabled_response
182- response . status = 400
183- 'The auto source feature is disabled.'
184- end
185-
186- def handle_stable_feed ( router , feed_id )
187- url = router . params [ 'url' ]
188- feed_token = router . params [ 'token' ]
189-
190- return bad_request_response ( 'URL parameter required' ) unless url
191- return bad_request_response ( 'URL too long' ) if url . length > 2048
192- return bad_request_response ( 'Invalid URL format' ) unless Auth . valid_url? ( url )
193-
194- return handle_public_feed_access ( router , feed_id , feed_token , url ) if feed_token
195-
196- handle_authenticated_feed_access ( router , url )
197- rescue StandardError => error
198- handle_auto_source_error ( error )
199- end
200-
201- def handle_authenticated_feed_access ( router , url )
202- token_data = Auth . authenticate ( router )
203- return unauthorized_response unless token_data
204-
205- return access_denied_response ( url ) unless AutoSource . url_allowed_for_token? ( token_data , url )
206-
207- strategy = router . params [ 'strategy' ] || 'ssrf_filter'
208- rss_content = AutoSource . generate_feed_content ( url , strategy )
209-
210- set_auto_source_headers
211- rss_content . to_s
212- end
213-
214- def handle_public_feed_access ( router , _feed_id , feed_token , url )
215- # Validate feed token and URL
216- return access_denied_response ( url ) unless Auth . feed_url_allowed? ( feed_token , url )
217-
218- strategy = router . params [ 'strategy' ] || 'ssrf_filter'
219- rss_content = AutoSource . generate_feed_content ( url , strategy )
220-
221- set_auto_source_headers
222- rss_content . to_s
223- rescue StandardError => error
224- handle_auto_source_error ( error )
225- end
226-
227- def handle_create_feed ( router )
228- return method_not_allowed_response unless router . post?
229-
230- token_data = Auth . authenticate ( router )
231- return unauthorized_response unless token_data
232-
233- url = router . params [ 'url' ]
234- return bad_request_response ( 'URL parameter required' ) unless url
235-
236- return access_denied_response ( url ) unless AutoSource . url_allowed_for_token? ( token_data , url )
237-
238- create_feed_response ( url , token_data , router . params )
239- rescue StandardError => error
240- handle_auto_source_error ( error )
241- end
242-
243- def create_feed_response ( url , token_data , params )
244- name = params [ 'name' ] || "Auto-generated feed for #{ url } "
245- strategy = params [ 'strategy' ] || 'ssrf_filter'
246-
247- feed_data = AutoSource . create_stable_feed ( name , url , token_data , strategy )
248- return internal_error_response unless feed_data
249-
250- response [ 'Content-Type' ] = 'application/json'
251- JSON . generate ( feed_data )
252- end
253-
254- def handle_list_feeds ( router )
255- token_data = Auth . authenticate ( router )
256- return unauthorized_response unless token_data
257-
258- # For stateless system, we can't list feeds without storage
259- # Return empty array for now
260- response [ 'Content-Type' ] = 'application/json'
261- JSON . generate ( [ ] )
262- end
263-
264- def handle_legacy_auto_source_feed ( router , encoded_url )
265- token_data = AutoSource . authenticate_with_token ( router )
266- return unauthorized_response unless token_data
267- return forbidden_origin_response unless AutoSource . allowed_origin? ( router )
268-
269- process_legacy_auto_source_request ( router , encoded_url , token_data )
270- rescue StandardError => error
271- handle_auto_source_error ( error )
272- end
273-
274- def process_legacy_auto_source_request ( router , encoded_url , token_data )
275- decoded_url = validate_and_decode_base64 ( encoded_url )
276- return bad_request_response ( 'Invalid URL encoding' ) unless decoded_url
277- return bad_request_response ( 'Invalid URL format' ) unless Auth . valid_url? ( decoded_url )
278- return access_denied_response ( decoded_url ) unless AutoSource . url_allowed_for_token? ( token_data , decoded_url )
279-
280- strategy = router . params [ 'strategy' ] || 'ssrf_filter'
281- rss_content = AutoSource . generate_feed ( encoded_url , strategy )
282- set_auto_source_headers
283- rss_content . to_s
284- end
285-
286- def handle_auto_source_error ( error )
287- response . status = 500
288- response [ 'Content-Type' ] = CONTENT_TYPE_RSS
289- AutoSource . error_feed ( error . message )
290- end
291-
292- # Health check route helpers
293- def handle_health_check ( router )
294- token_data = Auth . authenticate ( router )
295- health_check_account = HealthCheck . find_health_check_account
296-
297- if token_data && health_check_account && token_data [ :token ] == health_check_account [ :token ]
298- response [ 'Content-Type' ] = 'text/plain'
299- HealthCheck . run
300- else
301- health_check_unauthorized
302- end
303- end
304168 end
305169 end
306170end
0 commit comments