|
2 | 2 |
|
3 | 3 | require 'roda'
|
4 | 4 | require 'rack/cache'
|
5 |
| -require 'rack-timeout' |
6 |
| -require_relative 'app/health_check' |
7 |
| -require_relative 'app/local_config' |
8 |
| -require_relative 'app/html2rss_facade' |
9 | 5 |
|
10 | 6 | require_relative 'roda/roda_plugins/basic_auth'
|
11 | 7 |
|
12 |
| -module App |
13 |
| - ## |
14 |
| - # This app uses html2rss and serves the feeds via HTTP. |
15 |
| - # |
16 |
| - # It is built with [Roda](https://roda.jeremyevans.net/). |
17 |
| - class App < Roda |
18 |
| - opts[:check_dynamic_arity] = false |
19 |
| - opts[:check_arity] = :warn |
20 |
| - |
21 |
| - use Rack::Timeout |
22 |
| - |
23 |
| - use Rack::Cache, |
24 |
| - metastore: 'file:./tmp/rack-cache-meta', |
25 |
| - entitystore: 'file:./tmp/rack-cache-body', |
26 |
| - verbose: (ENV.fetch('RACK_ENV', nil) == 'development') |
27 |
| - |
28 |
| - plugin :content_security_policy do |csp| |
29 |
| - csp.default_src :none |
30 |
| - csp.style_src :self |
31 |
| - csp.script_src :self |
32 |
| - csp.connect_src :self |
33 |
| - csp.img_src :self |
34 |
| - csp.font_src :self |
35 |
| - csp.form_action :self |
36 |
| - csp.base_uri :none |
37 |
| - csp.frame_ancestors :none |
38 |
| - csp.block_all_mixed_content |
39 |
| - end |
40 |
| - |
41 |
| - plugin :default_headers, |
42 |
| - 'Content-Type' => 'text/html', |
43 |
| - 'X-Frame-Options' => 'deny', |
44 |
| - 'X-Content-Type-Options' => 'nosniff', |
45 |
| - 'X-XSS-Protection' => '1; mode=block' |
46 |
| - |
47 |
| - plugin :error_handler do |error| |
48 |
| - handle_error(error) |
49 |
| - end |
50 |
| - |
51 |
| - plugin :public |
52 |
| - plugin :render, escape: true, layout: 'layout' |
53 |
| - plugin :typecast_params |
54 |
| - plugin :basic_auth |
55 |
| - |
56 |
| - route do |r| |
57 |
| - path = RequestPath.new(request) |
| 8 | +module Html2rss |
| 9 | + module Web |
| 10 | + ## |
| 11 | + # This app uses html2rss and serves the feeds via HTTP. |
| 12 | + # |
| 13 | + # It is built with [Roda](https://roda.jeremyevans.net/). |
| 14 | + class App < Roda |
| 15 | + # TODO: move to helper |
| 16 | + def self.development? |
| 17 | + ENV['RACK_ENV'] == 'development' |
| 18 | + end |
58 | 19 |
|
59 |
| - r.root { view 'index' } |
| 20 | + def development? = self.class.development? |
| 21 | + |
| 22 | + opts[:check_dynamic_arity] = false |
| 23 | + opts[:check_arity] = :warn |
| 24 | + |
| 25 | + use Rack::Cache, |
| 26 | + metastore: 'file:./tmp/rack-cache-meta', |
| 27 | + entitystore: 'file:./tmp/rack-cache-body', |
| 28 | + verbose: development? |
| 29 | + |
| 30 | + plugin :content_security_policy do |csp| |
| 31 | + csp.default_src :none |
| 32 | + csp.style_src :self |
| 33 | + csp.script_src :self |
| 34 | + csp.connect_src :self |
| 35 | + csp.img_src :self |
| 36 | + csp.font_src :self |
| 37 | + csp.form_action :self |
| 38 | + csp.base_uri :none |
| 39 | + csp.frame_ancestors :none |
| 40 | + csp.block_all_mixed_content |
| 41 | + end |
60 | 42 |
|
61 |
| - r.public |
| 43 | + plugin :default_headers, |
| 44 | + 'Content-Type' => 'text/html', |
| 45 | + 'X-Frame-Options' => 'deny', |
| 46 | + 'X-Content-Type-Options' => 'nosniff', |
| 47 | + 'X-XSS-Protection' => '1; mode=block' |
62 | 48 |
|
63 |
| - r.get 'health_check.txt' do |
64 |
| - handle_health_check |
65 |
| - end |
| 49 | + plugin :exception_page |
| 50 | + plugin :error_handler do |error| |
| 51 | + next exception_page(error) if development? |
66 | 52 |
|
67 |
| - r.on String, String do |folder_name, config_name_with_ext| |
68 |
| - handle_html2rss_configs(path.full_config_name, folder_name, config_name_with_ext) |
| 53 | + handle_error(error) |
69 | 54 | end
|
70 | 55 |
|
71 |
| - r.on String do |config_name_with_ext| |
72 |
| - handle_local_config_feeds(path.full_config_name, config_name_with_ext) |
| 56 | + plugin :hash_branches |
| 57 | + plugin :public |
| 58 | + plugin :render, escape: true, layout: 'layout' |
| 59 | + plugin :typecast_params |
| 60 | + plugin :basic_auth |
| 61 | + |
| 62 | + Dir['routes/**/*.rb'].each do |f| |
| 63 | + if development? |
| 64 | + Unreloader.require f |
| 65 | + else |
| 66 | + require_relative f |
| 67 | + end |
73 | 68 | end
|
74 |
| - end |
75 | 69 |
|
76 |
| - private |
77 |
| - |
78 |
| - def handle_error(error) # rubocop:disable Metrics/MethodLength |
79 |
| - case error |
80 |
| - when Html2rss::Config::ParamsMissing, |
81 |
| - Roda::RodaPlugins::TypecastParams::Error |
82 |
| - set_error_response('Parameters missing or invalid', 422) |
83 |
| - when Html2rss::AttributePostProcessors::UnknownPostProcessorName, |
84 |
| - Html2rss::ItemExtractors::UnknownExtractorName, |
85 |
| - Html2rss::Config::ChannelMissing |
86 |
| - set_error_response('Invalid feed config', 422) |
87 |
| - when ::App::LocalConfig::NotFound, |
88 |
| - Html2rss::Configs::ConfigNotFound |
89 |
| - set_error_response('Feed config not found', 404) |
90 |
| - else |
91 |
| - set_error_response('Internal Server Error', 500) |
92 |
| - end |
| 70 | + route do |r| |
| 71 | + r.public |
93 | 72 |
|
94 |
| - @show_backtrace = ENV.fetch('RACK_ENV', nil) == 'development' |
95 |
| - @error = error |
96 |
| - view 'error' |
97 |
| - end |
| 73 | + r.hash_branches('') |
98 | 74 |
|
99 |
| - def set_error_response(page_title, status) |
100 |
| - @page_title = page_title |
101 |
| - response.status = status |
102 |
| - end |
| 75 | + r.root { view 'index' } |
103 | 76 |
|
104 |
| - def handle_health_check |
105 |
| - HttpCache.expires_now(response) |
| 77 | + r.get 'health_check.txt' do |
| 78 | + handle_health_check |
| 79 | + end |
106 | 80 |
|
107 |
| - with_basic_auth(realm: HealthCheck, |
108 |
| - username: HealthCheck::Auth.username, |
109 |
| - password: HealthCheck::Auth.password) do |
110 |
| - HealthCheck.run |
111 |
| - end |
112 |
| - end |
| 81 | + r.on String, String do |folder_name, config_name_with_ext| |
| 82 | + handle_html2rss_configs(request, folder_name, config_name_with_ext) |
| 83 | + end |
113 | 84 |
|
114 |
| - def handle_local_config_feeds(full_config_name, _config_name_with_ext) |
115 |
| - Html2rssFacade.from_local_config(full_config_name, typecast_params) do |config| |
116 |
| - response['Content-Type'] = 'text/xml' |
117 |
| - HttpCache.expires(response, config.ttl * 60, cache_control: 'public') |
| 85 | + r.on String do |config_name_with_ext| |
| 86 | + handle_local_config_feeds(request, config_name_with_ext) |
| 87 | + end |
118 | 88 | end
|
119 |
| - end |
120 | 89 |
|
121 |
| - def handle_html2rss_configs(full_config_name, _folder_name, _config_name_with_ext) |
122 |
| - Html2rssFacade.from_config(full_config_name, typecast_params) do |config| |
123 |
| - response['Content-Type'] = 'text/xml' |
124 |
| - HttpCache.expires(response, config.ttl * 60, cache_control: 'public') |
| 90 | + Dir['helpers/*.rb'].each do |f| |
| 91 | + if development? |
| 92 | + Unreloader.require f |
| 93 | + else |
| 94 | + require_relative f |
| 95 | + end |
125 | 96 | end
|
126 | 97 | end
|
127 | 98 | end
|
|
0 commit comments