# frozen_string_literal: true require_relative "models" require "roda" class App < Roda opts[:check_dynamic_arity] = false opts[:check_arity] = :warn plugin :default_headers, "Content-Type" => "text/html", # "Strict-Transport-Security" => "max-age=16070400;", # Uncomment if only allowing https:// access "X-Frame-Options" => "deny", "X-Content-Type-Options" => "nosniff", "X-XSS-Protection" => "1; mode=block" plugin :content_security_policy do |csp| csp.default_src :none # csp.style_src :self, "https://cdn.jsdelivr.net" csp.form_action :self csp.script_src :self csp.connect_src :self csp.base_uri :none csp.frame_ancestors :none end # css_opts = {cache: false, style: :compressed} # :nocov: # if ENV["RACK_ENV"] == 'development' # css_opts.merge!(source_map_embed: true, source_map_contents: true, source_map_file: ".") # end plugin :render_coverage if defined?(SimpleCov) # :nocov: plugin :route_csrf plugin :flash # plugin :assets, css: "app.scss", css_opts: css_opts, timestamp_paths: true # plugin :render, escape: true, layout: "./layout", :template_opts=>{chain_appends: !defined?(SimpleCov), freeze: true, skip_compiled_encoding_detection: true} plugin :render, engine: :phlex # plugin :public plugin :Integer_matcher_max plugin :typecast_params_sized_integers, sizes: [64], default_size: 64 plugin :hash_branch_view_subdir logger = if ENV["RACK_ENV"] == "test" Class.new{def write(_) end}.new else $stderr end plugin :common_logger, logger plugin :not_found do @page_title = "File Not Found" view(content: "") end if ENV["RACK_ENV"] == "development" plugin :exception_page class RodaRequest def assets exception_page_assets super end end else def self.freeze Sequel::Model.freeze_descendents DB.freeze super end end plugin :error_handler do |e| case e when Roda::RodaPlugins::RouteCsrf::InvalidToken @page_title = "Invalid Security Token" response.status = 400 view(content: "
An invalid security token was submitted with this request, and this request could not be processed.
") else $stderr.print "#{e.class}: #{e.message}\n" $stderr.puts e.backtrace next exception_page(e, assets: true) if ENV["RACK_ENV"] == "development" @page_title = "Internal Server Error" view(content: "") end end plugin :sessions, key: "_App.session", #cookie_options: {secure: ENV['RACK_ENV'] != 'test'}, # Uncomment if only allowing https:// access secret: ENV.send((ENV["RACK_ENV"] == "development" ? :[] : :delete), "APP_SESSION_SECRET") # if Unreloader.autoload? # plugin :autoload_hash_branches # autoload_hash_branch_dir("./routes") # end # Unreloader.autoload("routes", delete_hook: proc{|f| hash_branch(File.basename(f).delete_suffix(".rb"))}){} route do |r| # r.public # r.assets check_csrf! r.root do @page_title = "Foo" # render("hello", locals: {name: "Alice"}) view("hello", locals: {name: "Bob"}) end end end require "tilt/template" require "phlex" module Tilt class PhlexTemplate < Template def prepare end # I have no idea how this works - it's just copied pretty much blindly from here: # # https://github.com/phlex-ruby/phlex-rails/blob/main/lib/phlex/rails/layout.rb#L18 def evaluate(scope, locals, &block) klass = Class.new(Phlex::HTML) klass.class_eval(data, __FILE__, __LINE__) component = klass.new scope.instance_variables .reject { _2.start_with?("@_") } .each do |k| component.instance_variable_set(k, scope.instance_variable_get(k)) end locals.each do |k,v| component.instance_variable_set("@#{k}", v) end component.call do |yielded| output = yield component.unsafe_raw(output) end end # Not sure if this is better...? # def precompiled_preamble(*) = "klass = Class.new(Phlex::HTML) do" # def precompiled_template(*) = data # def precompiled_postamble(locals) = <<~RUBY # end # component = klass.new(**locals) # component.call do |yielded| # output = yield # component.unsafe_raw(output) # end # RUBY end end Tilt.register(Tilt::PhlexTemplate, "phlex")