225 lines
5.3 KiB
Crystal
225 lines
5.3 KiB
Crystal
require "http/server"
|
|
require "http/server/handler"
|
|
require "uri"
|
|
require "sqlite3"
|
|
require "migrate"
|
|
require "dotenv"
|
|
require "toml"
|
|
require "colorize"
|
|
require "option_parser"
|
|
require "migrate"
|
|
|
|
require "./server"
|
|
require "./enums"
|
|
require "./lib/hash"
|
|
require "./lib/format"
|
|
require "./lib/xor"
|
|
require "./lib/accounts"
|
|
require "./lib/gjp"
|
|
require "./lib/clean"
|
|
require "./lib/songs"
|
|
require "./lib/ids"
|
|
require "./lib/level"
|
|
require "./lib/dailies"
|
|
require "./lib/templates"
|
|
require "./lib/reupload"
|
|
require "./lib/creator_points"
|
|
require "./lib/versions"
|
|
require "./lib/ips"
|
|
require "./lib/ranks"
|
|
require "./lib/notifications"
|
|
|
|
require "./patch-exe.cr"
|
|
|
|
if File.exists?(".env")
|
|
Dotenv.load
|
|
end
|
|
|
|
include CrystalGauntlet::PatchExe
|
|
|
|
module CrystalGauntlet
|
|
VERSION = "0.1.0"
|
|
|
|
CONFIG = File.exists?("./config.toml") ? TOML.parse(File.read("./config.toml")) : TOML.parse("") # todo: log warning?
|
|
LOG = ::Log.for("crystal-gauntlet")
|
|
|
|
def config_get(key : String)
|
|
this = CONFIG
|
|
key.split(".").each do |val|
|
|
next_val = this.as(Hash)[val]?
|
|
if next_val == nil
|
|
return nil
|
|
else
|
|
this = next_val
|
|
end
|
|
end
|
|
return this
|
|
end
|
|
def config_get(key : String, default)
|
|
config_get(key).as?(typeof(default)) || default
|
|
end
|
|
|
|
DATABASE = DB.open(ENV["DATABASE_URL"]? || "sqlite3://./data/crystal-gauntlet.db")
|
|
|
|
# todo: unhardcore
|
|
DATA_FOLDER = Path.new("data")
|
|
|
|
@@endpoints = Hash(String, (HTTP::Server::Context -> String)).new
|
|
@@template_endpoints = Hash(
|
|
NamedTuple(name: String, path: String, methods: Enumerable(String)),
|
|
Proc(HTTP::Server::Context, Hash(String, String?), Nil)
|
|
).new
|
|
|
|
@@up_at = nil
|
|
|
|
def self.uptime
|
|
if !@@up_at
|
|
return Time::Span::ZERO
|
|
else
|
|
return Time.utc - @@up_at.not_nil!
|
|
end
|
|
end
|
|
|
|
def self.uptime_s
|
|
span = uptime
|
|
Format.fmt_timespan_long(span)
|
|
end
|
|
|
|
def self.endpoints
|
|
@@endpoints
|
|
end
|
|
|
|
def self.template_endpoints
|
|
@@template_endpoints
|
|
end
|
|
|
|
def self.is_funny
|
|
config_get("general.easter_eggs", false)
|
|
end
|
|
|
|
def severity_color(severity : Log::Severity) : Colorize::Object
|
|
case severity
|
|
when .trace?
|
|
Colorize.with.dark_gray
|
|
when .debug?
|
|
Colorize.with.dark_gray
|
|
when .info?
|
|
Colorize.with.cyan
|
|
when .notice?
|
|
Colorize.with.cyan
|
|
when .warn?
|
|
Colorize.with.yellow
|
|
when .error?
|
|
Colorize.with.red
|
|
when .fatal?
|
|
Colorize.with.light_red
|
|
else
|
|
Colorize.with.white
|
|
end
|
|
end
|
|
|
|
struct CrystalGauntletFormat < Log::StaticFormatter
|
|
def run
|
|
Colorize.with.light_gray.dim.surround(@io) do
|
|
timestamp
|
|
end
|
|
string " "
|
|
severity_color(@entry.severity).surround(@io) do
|
|
@entry.severity.label.rjust(@io, 6)
|
|
end
|
|
string " "
|
|
Colorize.with.white.surround(@io) do
|
|
source
|
|
end
|
|
string " "
|
|
message
|
|
end
|
|
end
|
|
|
|
def self.run()
|
|
Log.setup_from_env(backend: Log::IOBackend.new(formatter: CrystalGauntletFormat))
|
|
|
|
migrate = false
|
|
calc_creator_points = false
|
|
patch_exe = false
|
|
patch_exe_location = nil
|
|
patch_exe_package = nil
|
|
|
|
parser = OptionParser.new do |parser|
|
|
parser.banner = "Usage: crystal-gauntlet [command] [arguments]"
|
|
|
|
parser.on("migrate", "Migrate the database") do
|
|
migrate = true
|
|
parser.banner = "Usage: crystal-gauntlet migrate [arguments]"
|
|
end
|
|
parser.on("calc_creator_points", "Calculate creator points and update them") do
|
|
calc_creator_points = true
|
|
parser.banner = "Usage: crystal-gauntlet calc_creator_points [arguments]"
|
|
end
|
|
parser.on("patch_exe", "Patch Geometry Dash executables with your server URL (supports #{SUPPORTED_PATCH_PLATFORMS.join(", ")})") do
|
|
patch_exe = true
|
|
parser.banner = "Usage: crystal-gauntlet patch_exe <file>"
|
|
parser.on("-p NAME", "--package=NAME", "Specify the new package name") { |name| patch_exe_package = name }
|
|
parser.unknown_args do |opt|
|
|
patch_exe_location = opt[0]?
|
|
end
|
|
end
|
|
parser.on("-h", "--help", "Show this help") do
|
|
puts parser
|
|
exit
|
|
end
|
|
end
|
|
|
|
parser.parse
|
|
|
|
if patch_exe
|
|
if !patch_exe_location
|
|
puts parser
|
|
exit 1
|
|
end
|
|
check_server_length(true)
|
|
LOG.info { "Patching #{patch_exe_location}" }
|
|
patch_exe_file(patch_exe_location.not_nil!, patch_exe_package)
|
|
exit
|
|
end
|
|
|
|
migrator = Migrate::Migrator.new(
|
|
DATABASE
|
|
)
|
|
|
|
if migrate
|
|
LOG.info { "Migrating #{ENV["DATABASE_URL"].colorize(:white)}..." }
|
|
migrator.to_latest
|
|
exit
|
|
end
|
|
|
|
if calc_creator_points
|
|
LOG.info { "updating creator points" }
|
|
DATABASE.query_all("select id, username, creator_points from users", as: {Int32, String, Int32}).each() do |id, username, old_count|
|
|
new_count = CreatorPoints.update_creator_points id
|
|
if old_count > 0 || new_count > 0
|
|
LOG.info { "#{username}: #{old_count} -> #{new_count}" }
|
|
end
|
|
end
|
|
|
|
exit
|
|
end
|
|
|
|
if !migrator.latest?
|
|
LOG.fatal { "Database hasn\'t been migrated!! Please run #{"crystal-gauntlet migrate".colorize(:white)}" }
|
|
exit 1
|
|
end
|
|
|
|
["songs", "levels", "saves"].each() { |v|
|
|
Dir.mkdir_p(DATA_FOLDER / v)
|
|
}
|
|
|
|
Server.run()
|
|
end
|
|
end
|
|
|
|
require "./endpoints/**"
|
|
require "./template_endpoints/**"
|
|
|
|
CrystalGauntlet.run()
|