|
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