|
|
|
@ -1,159 +1,121 @@
|
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
require_relative "models"
|
|
|
|
|
require_relative "views"
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
module RankKing
|
|
|
|
|
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
|
|
|
|
|
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: "<p>An invalid security token was submitted with this request, and this request could not be processed.</p>")
|
|
|
|
|
# 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 :public
|
|
|
|
|
plugin :Integer_matcher_max
|
|
|
|
|
plugin :typecast_params_sized_integers, sizes: [64], default_size: 64
|
|
|
|
|
# plugin :hash_branch_view_subdir
|
|
|
|
|
plugin :custom_block_results
|
|
|
|
|
|
|
|
|
|
logger = if ENV["RACK_ENV"] == "test"
|
|
|
|
|
Class.new{def write(_) end}.new
|
|
|
|
|
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: "")
|
|
|
|
|
$stderr
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
plugin :common_logger, logger
|
|
|
|
|
|
|
|
|
|
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!
|
|
|
|
|
plugin :not_found do
|
|
|
|
|
@page_title = "File Not Found"
|
|
|
|
|
view(content: "")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
r.root do
|
|
|
|
|
@page_title = "Foo"
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
# render("hello", locals: {name: "Alice"})
|
|
|
|
|
view("hello", locals: {name: "Bob"})
|
|
|
|
|
plugin :error_handler do |e|
|
|
|
|
|
case e
|
|
|
|
|
when Roda::RodaPlugins::RouteCsrf::InvalidToken
|
|
|
|
|
@page_title = "Invalid Security Token"
|
|
|
|
|
response.status = 400
|
|
|
|
|
view(content: "<p>An invalid security token was submitted with this request, and this request could not be processed.</p>")
|
|
|
|
|
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
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
require "tilt/template"
|
|
|
|
|
require "phlex"
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
module Tilt
|
|
|
|
|
class PhlexTemplate < Template
|
|
|
|
|
def prepare
|
|
|
|
|
end
|
|
|
|
|
# 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"))}){}
|
|
|
|
|
|
|
|
|
|
# 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__)
|
|
|
|
|
# def render(view, layout=Views::Layout.new)
|
|
|
|
|
# component.call
|
|
|
|
|
# end
|
|
|
|
|
|
|
|
|
|
component = klass.new
|
|
|
|
|
route do |r|
|
|
|
|
|
# r.public
|
|
|
|
|
# r.assets
|
|
|
|
|
check_csrf!
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
r.root do
|
|
|
|
|
Views::Layout.new(Views::Hello.new(name: "Bob")).call
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
component.call do |yielded|
|
|
|
|
|
output = yield
|
|
|
|
|
component.unsafe_raw(output)
|
|
|
|
|
r.on "pools" do
|
|
|
|
|
r.is "new" do
|
|
|
|
|
Views::Layout.new(Views::NewPool.new).call
|
|
|
|
|
end
|
|
|
|
|
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")
|
|
|
|
|