14
14
require_relative 'app/response_helpers'
15
15
require_relative 'app/static_file_helpers'
16
16
require_relative 'app/xml_builder'
17
+ require_relative 'app/auto_source_routes'
18
+ require_relative 'app/health_check_routes'
17
19
18
20
module Html2rss
19
21
module Web
@@ -25,6 +27,8 @@ class App < Roda
25
27
include ApiRoutes
26
28
include ResponseHelpers
27
29
include StaticFileHelpers
30
+ include AutoSourceRoutes
31
+ include HealthCheckRoutes
28
32
29
33
CONTENT_TYPE_RSS = 'application/xml'
30
34
@@ -108,27 +112,28 @@ def self.production_error_message
108
112
109
113
plugin :exception_page
110
114
plugin :error_handler do |error |
111
- next exception_page ( error ) if ENV [ 'RACK_ENV' ] == ' development'
115
+ next exception_page ( error ) if development?
112
116
113
117
response . status = 500
114
- 'Internal Server Error'
118
+ response [ 'Content-Type' ] = CONTENT_TYPE_RSS
119
+ XmlBuilder . build_error_feed ( message : error . message )
115
120
end
116
121
117
122
plugin :public
118
123
plugin :hash_branches
119
124
120
- @show_backtrace = !ENV [ 'CI' ] . to_s . empty? || ( ENV [ 'RACK_ENV' ] == ' development' )
125
+ @show_backtrace = !ENV [ 'CI' ] . to_s . empty? || development?
121
126
122
127
# API routes
123
128
hash_branch 'api' do |r |
129
+ response [ 'Content-Type' ] = 'application/json'
130
+
124
131
r . on 'feeds.json' do
125
- response [ 'Content-Type' ] = 'application/json'
126
132
response [ 'Cache-Control' ] = 'public, max-age=300'
127
133
JSON . generate ( Feeds . list_feeds )
128
134
end
129
135
130
136
r . on 'strategies.json' do
131
- response [ 'Content-Type' ] = 'application/json'
132
137
response [ 'Cache-Control' ] = 'public, max-age=3600'
133
138
JSON . generate ( ApiRoutes . list_available_strategies )
134
139
end
@@ -147,160 +152,19 @@ def self.production_error_message
147
152
148
153
# Auto source routes
149
154
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 )
165
156
end
166
157
167
158
# Health check route
168
159
hash_branch 'health_check.txt' do |r |
169
- handle_health_check ( r )
160
+ handle_health_check_routes ( r )
170
161
end
171
162
172
163
route do |r |
173
164
r . public
174
165
r . hash_branches
175
166
handle_static_files ( r )
176
167
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
304
168
end
305
169
end
306
170
end
0 commit comments