migrate to athena for web routing

This commit is contained in:
Jill 2023-05-23 11:46:31 +03:00
parent 7e2dbd2917
commit 543a959829
Signed by: oat
GPG Key ID: 33489AA58A955108
15 changed files with 173 additions and 128 deletions

View File

@ -1,5 +1,9 @@
version: 2.0
shards:
athena-routing:
git: https://github.com/athena-framework/routing.git
version: 0.1.6
db:
git: https://github.com/crystal-lang/crystal-db.git
version: 0.11.0

View File

@ -23,6 +23,9 @@ dependencies:
branch: master
http-session:
github: straight-shoota/http-session
athena-routing:
github: athena-framework/routing
version: ~> 0.1.0
crystal: 1.6.2

View File

@ -9,6 +9,7 @@ require "colorize"
require "option_parser"
require "migrate"
require "./server"
require "./enums"
require "./lib/hash"
require "./lib/format"
@ -64,7 +65,10 @@ module CrystalGauntlet
DATA_FOLDER = Path.new("data")
@@endpoints = Hash(String, (HTTP::Server::Context -> String)).new
@@template_endpoints = Hash(String, (HTTP::Server::Context -> Nil)).new
@@template_endpoints = Hash(
NamedTuple(name: String, path: String, methods: Enumerable(String)),
Proc(HTTP::Server::Context, Hash(String, String?), Nil)
).new
@@up_at = nil
@ -132,90 +136,6 @@ module CrystalGauntlet
end
end
class GDHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
# expunge trailing slashes
path = context.request.path.chomp("/")
path = path.sub(config_get("general.append_path").as(String | Nil) || "", "")
body = context.request.body
if CrystalGauntlet.endpoints.has_key?(path) && context.request.method == "POST" && body
func = CrystalGauntlet.endpoints[path]
begin
value = func.call(context)
rescue err
LOG.error { "error while handling #{path.colorize(:white)}:" }
LOG.error { err.to_s }
is_relevant = true
err.backtrace.each do |str|
# this is a hack. Oh well
if str.starts_with?("src/crystal-gauntlet.cr") || (!is_relevant)
is_relevant = false
else
LOG.error {" #{str}"}
end
end
context.response.content_type = "text/plain"
context.response.respond_with_status(500, "-1")
else
max_size = 2048
value_displayed = value
if value.size > max_size
value_displayed = value[0..max_size] + ("".colorize(:dark_gray).to_s)
end
LOG.debug { "-> ".colorize(:green).to_s + value_displayed }
context.response.content_type = "text/plain"
# to let endpoints manually write to IO
if value != ""
context.response.print value
end
end
else
call_next(context)
end
end
end
class TemplateHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
# expunge trailing slashes
path = context.request.path.chomp("/")
body = context.request.body
if CrystalGauntlet.template_endpoints.has_key?(path)
func = CrystalGauntlet.template_endpoints[path]
begin
func.call(context)
rescue err
LOG.error { "error while handling #{path.colorize(:white)}:" }
LOG.error { err.to_s }
is_relevant = true
err.backtrace.each do |str|
# this is a hack. Oh well
if str.starts_with?("src/crystal-gauntlet.cr") || (!is_relevant)
is_relevant = false
else
LOG.error {" #{str}"}
end
end
context.response.content_type = "text/html"
context.response.respond_with_status(500, "Internal server error occurred, sorry about that")
end
else
call_next(context)
end
end
end
def self.run()
Log.setup_from_env(backend: Log::IOBackend.new(formatter: CrystalGauntletFormat))
@ -294,31 +214,7 @@ module CrystalGauntlet
Dir.mkdir_p(DATA_FOLDER / v)
}
server = HTTP::Server.new([
HTTP::LogHandler.new,
HTTP::StaticFileHandler.new("public/", fallthrough: true, directory_listing: false),
HTTP::StaticFileHandler.new((DATA_FOLDER / "songs").to_s, fallthrough: true, directory_listing: false),
CrystalGauntlet::GDHandler.new,
CrystalGauntlet::TemplateHandler.new
])
listen_on = URI.parse(ENV["LISTEN_ON"]? || "http://localhost:8080").normalize
case listen_on.scheme
when "http"
server.bind_tcp(listen_on.hostname.not_nil!, listen_on.port.not_nil!)
when "unix"
server.bind_unix(listen_on.to_s.sub("unix://",""))
end
check_server_length(false)
Reupload.init()
Ranks.init()
@@up_at = Time.utc
LOG.notice { "Listening on #{listen_on.to_s.colorize(:white)}" }
server.listen
Server.run()
end
end

93
src/server.cr Normal file
View File

@ -0,0 +1,93 @@
require "athena-routing"
module CrystalGauntlet::Server
class GDHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
# expunge trailing slashes
path = context.request.path.chomp("/")
path = path.sub(config_get("general.append_path").as(String | Nil) || "", "")
body = context.request.body
if CrystalGauntlet.endpoints.has_key?(path) && context.request.method == "POST" && body
func = CrystalGauntlet.endpoints[path]
begin
value = func.call(context)
rescue err
LOG.error { "error while handling #{path.colorize(:white)}:" }
LOG.error { err.to_s }
is_relevant = true
err.backtrace.each do |str|
# this is a hack. Oh well
if str.starts_with?("src/crystal-gauntlet.cr") || (!is_relevant)
is_relevant = false
else
LOG.error {" #{str}"}
end
end
context.response.content_type = "text/plain"
context.response.respond_with_status(500, "-1")
else
max_size = 2048
value_displayed = value
if value.size > max_size
value_displayed = value[0..max_size] + ("".colorize(:dark_gray).to_s)
end
LOG.debug { "-> ".colorize(:green).to_s + value_displayed }
context.response.content_type = "text/plain"
# to let endpoints manually write to IO
if value != ""
context.response.print value
end
end
else
call_next(context)
end
end
end
def self.run()
template_handler = ART::RoutingHandler.new
CrystalGauntlet.template_endpoints.each do |key, handler|
template_handler.add(
key[:name],
ART::Route.new(
key[:path],
methods: key[:methods]
)
) { |ctx, params| handler.call(ctx, params) }
end
server = HTTP::Server.new([
HTTP::LogHandler.new,
HTTP::StaticFileHandler.new("public/", fallthrough: true, directory_listing: false),
HTTP::StaticFileHandler.new((DATA_FOLDER / "songs").to_s, fallthrough: true, directory_listing: false),
GDHandler.new,
template_handler.compile
])
listen_on = URI.parse(ENV["LISTEN_ON"]? || "http://localhost:8080").normalize
case listen_on.scheme
when "http"
server.bind_tcp(listen_on.hostname.not_nil!, listen_on.port.not_nil!)
when "unix"
server.bind_unix(listen_on.to_s.sub("unix://",""))
end
check_server_length(false)
Reupload.init()
Ranks.init()
@@up_at = Time.utc
LOG.notice { "Listening on #{listen_on.to_s.colorize(:white)}" }
server.listen
end
end

View File

@ -1,11 +1,19 @@
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/#{config_get("general.append_path").as(String | Nil) || ""}accounts/accountManagement.php"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "account_management_redirect",
path: "/#{config_get("general.append_path").as(String | Nil) || ""}accounts/accountManagement.php",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.headers.add("Location", "/accounts/")
context.response.status = HTTP::Status::MOVED_PERMANENTLY
}
CrystalGauntlet.template_endpoints["/accounts"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "account_management",
path: "/accounts",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
account_id = nil

View File

@ -2,7 +2,12 @@ require "uri"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/accounts/settings"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "account_settings",
path: "/accounts/settings",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
account_id = nil

View File

@ -3,7 +3,11 @@ require "compress/gzip"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/tools/create_session"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "create_session",
path: "/tools/create_session",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
disabled = !config_get("sessions.allow").as(Bool | Nil)
result = nil
body = context.request.body

View File

@ -2,12 +2,21 @@ require "ecr"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/tools"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "tools_redirect",
path: "/tools",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.headers.add("Location", "/")
context.response.status = HTTP::Status::TEMPORARY_REDIRECT
}
CrystalGauntlet.template_endpoints[""] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "index",
path: "/",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
ECR.embed("./public/template/index.ecr", context.response)
}

View File

@ -4,7 +4,11 @@ include CrystalGauntlet
levels_per_page = 10
CrystalGauntlet.template_endpoints["/tools/levels"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "list_levels",
path: "/tools/levels",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
page = (context.request.query_params["page"]? || "0").to_i? || 0
total_levels = DATABASE.scalar("select count(*) from levels").as(Int64)

View File

@ -3,7 +3,11 @@ require "http-session"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/login"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "login",
path: "/login",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
if session = CrystalGauntlet.sessions.get(context)
logged_in = true
account_id = session.account_id

View File

@ -3,12 +3,11 @@ require "http-session"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/accounts/logout"] = ->(context : HTTP::Server::Context) {
if context.request.method != "POST"
context.response.respond_with_status 405
return
end
CrystalGauntlet.template_endpoints[{
name: "logout",
path: "/accounts/logout",
methods: ["post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
CrystalGauntlet.sessions.delete(context)
context.response.headers.add("Location", "/")

View File

@ -1,6 +1,10 @@
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/accounts/notifications"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "account_notifications",
path: "/accounts/notifications",
methods: ["get"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
account_id = nil

View File

@ -3,7 +3,11 @@ require "xml"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/tools/reupload"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "reupload",
path: "/tools/reupload",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
disabled = !(config_get("reuploads.allowed").as?(Bool))

View File

@ -2,7 +2,11 @@ require "ecr"
include CrystalGauntlet
CrystalGauntlet.template_endpoints["/tools/song_search"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "song_search",
path: "/tools/song_search",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
error = nil

View File

@ -17,7 +17,11 @@ def get_next_song_id() : Int32
end
end
CrystalGauntlet.template_endpoints["/tools/song_upload"] = ->(context : HTTP::Server::Context) {
CrystalGauntlet.template_endpoints[{
name: "song_upload",
path: "/tools/song_upload",
methods: ["get", "post"]
}] = ->(context : HTTP::Server::Context, params : Hash(String, String?)) {
context.response.content_type = "text/html"
disabled = !(config_get("songs.allow_custom_songs").as?(Bool))