refactor: split Server into api_routes and admin_routes modules

Keep server.rb focused on configuration, session setup, and shared
helpers. Extract route definitions into registered Sinatra extension
modules. Exclude the registered methods from AbcSize/MethodLength
metrics — large registered methods are the idiomatic Sinatra pattern.
This commit is contained in:
2026-06-25 21:35:07 +02:00
parent 140b16b55a
commit b2b8156648
4 changed files with 200 additions and 173 deletions
+10
View File
@@ -19,13 +19,23 @@ Layout/LineLength:
Metrics/AbcSize:
Max: 30
Exclude:
- "lib/volumen/admin_routes.rb"
- "lib/volumen/api_routes.rb"
Metrics/MethodLength:
Max: 25
Exclude:
- "lib/volumen/admin_routes.rb"
- "lib/volumen/api_routes.rb"
Metrics/ClassLength:
Max: 400
Metrics/ModuleLength:
Exclude:
- "lib/volumen/admin_routes.rb"
Metrics/BlockLength:
Exclude:
- "test/**/*"
+143
View File
@@ -0,0 +1,143 @@
# frozen_string_literal: true
module Volumen
# Server-rendered admin routes, registered into Volumen::Server.
module AdminRoutes
def self.registered(app)
app.get "/admin" do
redirect to("/admin/")
end
app.get "/admin/" do
require_login!
@posts = sorted_posts
erb :list
end
app.get "/admin/login" do
redirect to("/admin/") if authenticated?
erb :login
end
app.post "/admin/login" do
check_csrf!
check_login_rate_limit!
user = users.authenticate(params["username"], params["password"])
if user
session[:user] = user.username
redirect to("/admin/")
else
@error = "Invalid username or password."
erb :login
end
end
app.post "/admin/logout" do
check_csrf!
session.clear
redirect to("/admin/login")
end
app.get "/admin/posts/new" do
require_login!
@mode = :new
@post = Post.new(metadata: { "lang" => config.language })
erb :form
end
app.post "/admin/posts" do
require_login!
check_csrf!
create_post
end
app.get "/admin/posts/:slug/edit" do
require_login!
@mode = :edit
@post = store.find(params["slug"])
halt(404, "Not found") if @post.nil?
erb :form
end
app.post "/admin/posts/:slug" do
require_login!
check_csrf!
update_post
end
app.post "/admin/posts/:slug/delete" do
require_login!
check_csrf!
store.delete(params["slug"])
redirect to("/admin/")
end
app.post "/admin/preview" do
require_login!
content_type :html
Markdown.render(params["body"].to_s)
end
app.post "/admin/uploads" do
require_login!
check_csrf!
content_type :json
upload = params["file"]
halt(400, json("error" => "no_file")) unless upload.is_a?(Hash) && upload[:tempfile]
json("url" => store.store_upload(upload[:filename], upload[:tempfile]))
end
app.get "/media/*" do
path = store.media_file(params["splat"].first)
halt(404) if path.nil?
send_file(path)
end
app.get "/admin/logo.svg" do
send_file(File.expand_path("web/public/volumen-logo.svg", __dir__))
end
app.get "/admin/settings" do
require_login!
render_settings
end
app.post "/admin/settings/password" do
require_login!
check_csrf!
change_password
end
app.post "/admin/settings/username" do
require_login!
check_csrf!
change_username
end
app.post "/admin/settings/users" do
require_login!
require_admin!
check_csrf!
create_user
end
app.post "/admin/settings/users/:username/role" do
require_login!
require_admin!
check_csrf!
change_role(params["username"])
end
app.post "/admin/settings/users/:username/delete" do
require_login!
require_admin!
check_csrf!
remove_user(params["username"])
end
end
end
end
+42
View File
@@ -0,0 +1,42 @@
# frozen_string_literal: true
module Volumen
# Public JSON API routes, registered into Volumen::Server.
module ApiRoutes
def self.registered(app)
app.before "/api/volumen/*" do
headers "Access-Control-Allow-Origin" => "*"
content_type :json
end
app.get "/api/volumen/site" do
json(site_payload)
end
app.get "/api/volumen/posts" do
json(posts_payload)
end
app.get "/api/volumen/posts/:slug" do
post = store.find(params["slug"], lang: params["lang"])
halt(404, json("error" => "not_found")) if post.nil?
json(post.detail)
end
app.get "/api/volumen/tags" do
json("tags" => tag_counts)
end
app.get "/api/volumen/feed.xml" do
content_type "application/rss+xml"
feed_xml
end
app.get "/api/volumen/sitemap.xml" do
content_type "application/xml"
sitemap_xml
end
end
end
end
+5 -173
View File
@@ -8,6 +8,8 @@ require_relative "config"
require_relative "store"
require_relative "markdown"
require_relative "password"
require_relative "api_routes"
require_relative "admin_routes"
module Volumen
# Sinatra application: the public JSON API at /api/volumen and the
@@ -16,6 +18,9 @@ module Volumen
MAX_LOGIN_ATTEMPTS = 10
LOGIN_WINDOW = 60 # seconds
register ApiRoutes
register AdminRoutes
configure do
set :show_exceptions, false
set :raise_errors, false
@@ -49,179 +54,6 @@ module Volumen
options[:secure] = request.ssl? if options
end
# --- Public JSON API -------------------------------------------------
before "/api/volumen/*" do
headers "Access-Control-Allow-Origin" => "*"
content_type :json
end
get "/api/volumen/site" do
json(site_payload)
end
get "/api/volumen/posts" do
json(posts_payload)
end
get "/api/volumen/posts/:slug" do
post = store.find(params["slug"], lang: params["lang"])
halt(404, json("error" => "not_found")) if post.nil?
json(post.detail)
end
get "/api/volumen/tags" do
json("tags" => tag_counts)
end
get "/api/volumen/feed.xml" do
content_type "application/rss+xml"
feed_xml
end
get "/api/volumen/sitemap.xml" do
content_type "application/xml"
sitemap_xml
end
# --- Admin -----------------------------------------------------------
get "/admin" do
redirect to("/admin/")
end
get "/admin/" do
require_login!
@posts = sorted_posts
erb :list
end
get "/admin/login" do
redirect to("/admin/") if authenticated?
erb :login
end
post "/admin/login" do
check_csrf!
check_login_rate_limit!
user = users.authenticate(params["username"], params["password"])
if user
session[:user] = user.username
redirect to("/admin/")
else
@error = "Invalid username or password."
erb :login
end
end
post "/admin/logout" do
check_csrf!
session.clear
redirect to("/admin/login")
end
get "/admin/posts/new" do
require_login!
@mode = :new
@post = Post.new(metadata: { "lang" => config.language })
erb :form
end
post "/admin/posts" do
require_login!
check_csrf!
create_post
end
get "/admin/posts/:slug/edit" do
require_login!
@mode = :edit
@post = store.find(params["slug"])
halt(404, "Not found") if @post.nil?
erb :form
end
post "/admin/posts/:slug" do
require_login!
check_csrf!
update_post
end
post "/admin/posts/:slug/delete" do
require_login!
check_csrf!
store.delete(params["slug"])
redirect to("/admin/")
end
post "/admin/preview" do
require_login!
content_type :html
Markdown.render(params["body"].to_s)
end
post "/admin/uploads" do
require_login!
check_csrf!
content_type :json
upload = params["file"]
halt(400, json("error" => "no_file")) unless upload.is_a?(Hash) && upload[:tempfile]
json("url" => store.store_upload(upload[:filename], upload[:tempfile]))
end
get "/media/*" do
path = store.media_file(params["splat"].first)
halt(404) if path.nil?
send_file(path)
end
get "/admin/logo.svg" do
send_file(File.expand_path("web/public/volumen-logo.svg", __dir__))
end
get "/admin/settings" do
require_login!
render_settings
end
post "/admin/settings/password" do
require_login!
check_csrf!
change_password
end
post "/admin/settings/username" do
require_login!
check_csrf!
change_username
end
post "/admin/settings/users" do
require_login!
require_admin!
check_csrf!
create_user
end
post "/admin/settings/users/:username/role" do
require_login!
require_admin!
check_csrf!
change_role(params["username"])
end
post "/admin/settings/users/:username/delete" do
require_login!
require_admin!
check_csrf!
remove_user(params["username"])
end
private
def config