Websites/app/controllers/application_controller.rb
Donovan Daniels b1c702e3cd
Add image management ui
poorly tested but it worked well enough, I'm sure I'll be patching bugs over the next few weeks
Also remove turbo because it sucks
Also changed the way we handle hosts in dev
2024-05-06 03:25:17 -05:00

258 lines
7.3 KiB
Ruby

# frozen_string_literal: true
class ApplicationController < ActionController::Base
class ReadOnlyError < StandardError; end
class FeatureUnavailableError < StandardError; end
before_action :set_view_path
before_action :initialize_session
before_action :normalize_search
before_action :set_common_headers
helper_method :site_domain, :assets_path, :safe_site_name, :site_title, :site_color, :controller_param, :action_param, :body_class, :stimulus_class
skip_before_action :verify_authenticity_token
rescue_from Exception, with: :rescue_exception
def set_view_path
prepend_view_path(Rails.root.join("app", "views", safe_site_name))
end
def set_common_headers
Websites.config.common_headers(request.domain).each do |key, value|
response.headers[key] = value
end
end
def initialize_session
WebLogger.initialize(request)
CurrentUser.user = APIUser.anonymous
CurrentUser.ip_addr = request.remote_ip
end
def site_domain
"unknown"
end
def assets_path
site_domain
end
def site_title
"Unknown"
end
def site_color
"#2C2F33"
end
def safe_site_name
site_domain.parameterize.dasherize
end
def controller_param
return "unknown" unless params[:controller]
name = params[:controller].parameterize.dasherize
name = name.gsub("#{safe_site_name}-", "") if name.include?(safe_site_name)
name
end
def action_param
return "unknown" unless params[:action]
params[:action]
end
def body_class
"s-#{safe_site_name} c-#{controller_param} a-#{action_param}"
end
def stimulus_class
params[:controller].gsub("/", "--").dasherize
end
def track_usage(service)
APIUsage.create!(
user_id: CurrentUser.is_anonymous? ? nil : CurrentUser.id,
api_key: @apikey.nil? || @apikey.is_anon? ? nil : @apikey,
user_agent: request.headers["user-agent"],
method: request.method,
path: request.path,
params: request.params.to_json,
service: service,
ip_addr: request.remote_ip,
)
end
module CommonAssetRoutes
def manifest
render(partial: "manifest", layout: false)
end
def browserconfig
render(partial: "browserconfig", layout: false)
end
end
module CommonRoutes
def online
render(json: {
success: true,
uptime: Time.now - Websites::STARTED_AT,
})
end
def access_denied(message: nil, code: nil)
@message = message.present? ? "Access Denied: #{message}" : "Access Denied"
@code = code
respond_to do |fmt|
fmt.html { render("static/access_denied", status: 403) }
fmt.json do
render(json: {
success: false,
message: @message,
code: @code,
}, status: 403)
end
end
end
def not_found(code: nil)
@code = code
respond_to do |fmt|
fmt.any { render("static/not_found", status: 404) }
fmt.json do
render(json: {
success: false,
message: "Not found.",
code: @code,
}, status: 404)
end
end
end
def readonly
respond_to do |fmt|
fmt.html { render("static/readonly", status: YiffyAPIErrorCodes::READONLY.status) }
fmt.json { render_error(YiffyAPIErrorCodes::READONLY, message: "This service is currently in read only mode. Try again later.") }
end
end
private
def render_error(*, **)
extend(YiffyAPIUtil).render_error(*, **)
end
end
module RenderMethods
def handle_error
@exception = request.env["action_dispatch.exception"]
@status_code = @exception.try(:status_code) ||
ActionDispatch::ExceptionWrapper.new(
request.env, @exception
).status_code
return not_found if @status_code == 404
return rescue_exception(@exception) if @exception
render_error_page(@status_code, Exception.new("An unexpected error occurred."))
end
def rescue_exception(exception)
@exception = exception
case exception
when ActiveRecord::RecordNotFound
not_found
when ReadOnlyError
readonly
when PG::Error
render_error_page(503, exception, message: "The database is unavailable. Try again later.")
when ActionController::ParameterMissing, ActionController::UnpermittedParameters
render_expected_error(400, exception.message)
when ActionController::RoutingError
render_error_page(405, exception)
when ActionController::UnknownFormat, ActionView::MissingTemplate
render_unsupported_format
when ActionController::InvalidAuthenticityToken
access_denied(message: "Invalid CSRF Token")
else
render_error_page(500, exception)
end
end
def render_unsupported_format
return not_found if request.format.nil?
render_expected_error(406, "#{request.format} is not a supported format for this page", format: :html)
end
def render_expected_error(status, message, format: request.format.symbol)
format = :html unless format.in?(%i[html json])
@message = message
@log_code = nil
render("static/error", status: status, formats: format)
end
def render_error_page(status, exception, message: exception.message, format: request.format.symbol)
@exception = exception
@expected = status < 500
@message = message.encode("utf-8", invalid: :replace, undef: :replace)
@backtrace = Rails.backtrace_cleaner.clean(@exception.backtrace)
format = :html unless format.in?(%i[html json])
@message = "An unexpected error occurred." if !(Rails.env.development? || CurrentUser.is_admin?) && message == exception.message
WebLogger.log_exception(@exception, expected: @expected)
log = ExceptionLog.add(exception, request) unless @expected
@log_code = log&.code
render("static/error", status: status, formats: format)
end
end
module SearchMethods
def normalize_search
return unless request.get? || request.head?
params[:search] ||= ActionController::Parameters.new
deep_reject_blank = ->(hash) do
hash.reject { |_k, v| v.blank? || (v.is_a?(Hash) && deep_reject_blank.call(v).blank?) }
end
if params[:search].is_a?(ActionController::Parameters)
nonblank_search_params = deep_reject_blank.call(params[:search])
else
nonblank_search_params = ActionController::Parameters.new
end
if nonblank_search_params != params[:search]
params[:search] = nonblank_search_params
redirect_to(url_for(params: params.except(:controller, :action, :index).permit!))
end
end
def search_params
params.fetch(:search, {}).permit!
end
def permit_search_params(permitted_params)
params.fetch(:search, {}).permit(%i[id created_at updated_at] + permitted_params)
end
end
module ReadonlyMethods
extend ActiveSupport::Concern
def enforce_readonly
return unless Websites.config.readonly?
raise(ReadOnlyError) unless allowed_readonly_actions.include?(action_name)
end
def allowed_readonly_actions
%w[index show]
end
included do
before_action :enforce_readonly
end
end
include CommonRoutes
include RenderMethods
include SearchMethods
include Pagy::Backend
end