diff --git a/justfile b/justfile index 080ed94..6f7033c 100644 --- a/justfile +++ b/justfile @@ -19,6 +19,16 @@ dev: @echo "→ Starting volumen (dev) on http://localhost:9090 …" bundle exec exe/volumen serve --config ./config.toml --content ./posts +# Lint the codebase (RuboCop, zero offenses) +lint: + @echo "→ Linting…" + bundle exec rubocop + +# Auto-fix lint issues +fmt: + @echo "→ Auto-fixing…" + bundle exec rubocop -A + # Lint and package the gem (must pass with zero warnings) build: @echo "→ Linting…" diff --git a/lib/volumen/admin_routes.rb b/lib/volumen/admin_routes.rb index 59a555c..b57e7fa 100644 --- a/lib/volumen/admin_routes.rb +++ b/lib/volumen/admin_routes.rb @@ -88,16 +88,7 @@ module Volumen upload = params["file"] halt(400, json("error" => "no_file")) unless upload.is_a?(Hash) && upload[:tempfile] - if upload[:tempfile].size > Server::MAX_UPLOAD_BYTES - halt(413, json("error" => "file_too_large", "max_bytes" => Server::MAX_UPLOAD_BYTES)) - end - - content_type_str = upload[:type].to_s.split(";").first.to_s.strip.downcase - unless Server::ALLOWED_UPLOAD_TYPES.include?(content_type_str) - halt(415, json("error" => "unsupported_media_type", - "allowed" => Server::ALLOWED_UPLOAD_TYPES)) - end - + validate_upload!(upload) json("url" => store.store_upload(upload[:filename], upload[:tempfile])) end diff --git a/lib/volumen/server.rb b/lib/volumen/server.rb index 0ec93cf..e725978 100644 --- a/lib/volumen/server.rb +++ b/lib/volumen/server.rb @@ -83,8 +83,9 @@ module Volumen end def published_posts - @published_posts ||= store.all.reject { |post| post.draft? || post.scheduled? } - .sort_by { |post| post.date_string || "" }.reverse + @published_posts ||= store.all + .reject { |post| post.draft? || post.scheduled? } + .sort_by { |post| post.date_string || "" }.reverse end def site_payload @@ -302,11 +303,22 @@ module Volumen def check_csrf! token = params["_csrf"] - return if token && session[:csrf] && Rack::Utils.secure_compare(session[:csrf], token) + return if token && session["csrf"] && Rack::Utils.secure_compare(session["csrf"], token) halt(403, "Invalid CSRF token") end + def validate_upload!(upload) + if upload[:tempfile].size > MAX_UPLOAD_BYTES + halt(413, json("error" => "file_too_large", "max_bytes" => MAX_UPLOAD_BYTES)) + end + + content_type = upload[:type].to_s.split(";").first.to_s.strip.downcase + return if ALLOWED_UPLOAD_TYPES.include?(content_type) + + halt(415, json("error" => "unsupported_media_type", "allowed" => ALLOWED_UPLOAD_TYPES)) + end + def check_login_rate_limit! Volumen.sweep_login_attempts! ip = request.ip.to_s