diff --git a/db/migrations/10_next_ids.sql b/db/migrations/10_next_ids.sql new file mode 100644 index 0000000..430a1e6 --- /dev/null +++ b/db/migrations/10_next_ids.sql @@ -0,0 +1,8 @@ +-- +migrate up +CREATE TABLE next_id ( + name TEXT NOT NULL, + id INTEGER NOT NULL DEFAULT 0 +); + +-- +migrate down +DROP TABLE next_id; \ No newline at end of file diff --git a/src/crystal-gauntlet.cr b/src/crystal-gauntlet.cr index aac999e..51d5ebd 100644 --- a/src/crystal-gauntlet.cr +++ b/src/crystal-gauntlet.cr @@ -14,6 +14,7 @@ require "./lib/accounts" require "./lib/gjp" require "./lib/clean" require "./lib/songs" +require "./lib/ids" Dotenv.load @@ -94,7 +95,7 @@ module CrystalGauntlet body = context.request.body - if CrystalGauntlet.endpoints.has_key?(path) && body + if CrystalGauntlet.endpoints.has_key?(path) && context.request.method == "POST" && body func = CrystalGauntlet.endpoints[path] value = func.call(body.gets_to_end) LOG.debug { "-> " + value } diff --git a/src/endpoints/accounts/registerAccount.cr b/src/endpoints/accounts/registerAccount.cr index 303110d..c9d420d 100644 --- a/src/endpoints/accounts/registerAccount.cr +++ b/src/endpoints/accounts/registerAccount.cr @@ -26,10 +26,10 @@ CrystalGauntlet.endpoints["/accounts/registerGJAccount.php"] = ->(body : String) password_hash = Crypto::Bcrypt::Password.create(password, cost: 10).to_s gjp2 = CrystalGauntlet::GJP.hash(password) - next_id = (DATABASE.scalar("select max(id) from accounts").as(Int64 | Nil) || 0) + 1 + next_id = IDs.get_next_id("accounts") DATABASE.exec "insert into accounts (id, username, password, email, gjp2) values (?, ?, ?, ?, ?)", next_id, username, password_hash, email, gjp2 - user_id = (DATABASE.scalar("select max(id) from users").as(Int64 | Nil) || 0) + 1 + user_id = IDs.get_next_id("users") DATABASE.exec "insert into users (id, account_id, username, registered) values (?, ?, ?, 1)", user_id, next_id, username "1" } diff --git a/src/endpoints/levels/addLevelComment.cr b/src/endpoints/levels/addLevelComment.cr index 6d50d8e..cea1763 100644 --- a/src/endpoints/levels/addLevelComment.cr +++ b/src/endpoints/levels/addLevelComment.cr @@ -22,7 +22,7 @@ CrystalGauntlet.endpoints["/uploadGJComment21.php"] = ->(body : String): String if comment && comment != "" # todo: cap comment size comment_value = Base64.decode_string comment # usual b64, surprisingly - next_id = (DATABASE.scalar("select max(id) from comments").as(Int64 | Nil) || 0) + 1 + next_id = IDs.get_next_id("comments") DATABASE.exec("insert into comments (id, level_id, user_id, comment, percent) values (?, ?, ?, ?, ?)", next_id, level_id, user_id, comment_value, percent) return "1" else diff --git a/src/endpoints/levels/downloadLevel.cr b/src/endpoints/levels/downloadLevel.cr index 8f470ad..d0acc9b 100644 --- a/src/endpoints/levels/downloadLevel.cr +++ b/src/endpoints/levels/downloadLevel.cr @@ -98,7 +98,7 @@ CrystalGauntlet.endpoints["/downloadGJLevel22.php"] = ->(body : String): String }) response << Hashes.gen_solo(level_data) - thing = [user_id, stars || 0, (difficulty && difficulty.demon?) || 0, id, rated_coins, featured, password, 0].map! { |x| Format.fmt_value(x) } + thing = [user_id, stars || 0, (difficulty && difficulty.demon?) || 0, id, rated_coins, featured, password, 0].map { |x| Format.fmt_value(x) } response << Hashes.gen_solo_2(thing.join(",")) return response.join("#") diff --git a/src/endpoints/levels/getLevels.cr b/src/endpoints/levels/getLevels.cr index e311e53..84dee7f 100644 --- a/src/endpoints/levels/getLevels.cr +++ b/src/endpoints/levels/getLevels.cr @@ -44,7 +44,7 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { end if params["song"]? if params["customSong"]? && params["customSong"]? != "" - # todo + queryParams << "song_id = '#{params["customSong"].to_i}'" else queryParams << "song_id = '#{params["song"].to_i}'" end @@ -86,7 +86,8 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { queryParams << "difficulty = #{LevelDifficulty::Auto.value} or (difficulty is null and community_difficulty = #{LevelDifficulty::Auto.value})" else # easy, normal, hard, harder, insane - # todo + diffs = params["diff"].split(",").map{ |v| v.to_i }.join(",") + queryParams << "difficulty in (#{diffs}) or (difficulty is null and community_difficulty in (#{diffs}))" end end @@ -99,7 +100,9 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { when "2" # most liked order = "likes desc" when "3" # trending - # todo + # todo: make configurable? + order = "likes desc" + queryParams << "created_at > #{(Time.utc - 7.days).to_s(Format::TIME_FORMAT)}" when "5" # made by user queryParams << "levels.user_id = #{searchQuery.to_i}" # (you can't sql inject with numbers) when "6", "17" # featured (gdw is 17) @@ -109,7 +112,8 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { # todo: order by epic date queryParams << "epic = 1" when "7" # magic - # todo + # todo: make configurable? + queryParams << "objects > 4000" when "10", "19" # map packs order = "map_pack_links.idx asc" queryParams << "levels.id in (#{searchQuery.split(",").map{|v| v.to_i}.join(",")})" @@ -215,7 +219,6 @@ CrystalGauntlet.endpoints["/getGJLevels21.php"] = ->(body : String): String { searchMeta = "#{level_count}:#{page_offset}:#{levels_per_page}" res = [results.join("|"), users.join("|"), songs.join("~:~"), searchMeta, CrystalGauntlet::Hashes.gen_multi(hash_data)].join("#") - LOG.debug { res } res } diff --git a/src/endpoints/levels/uploadLevel.cr b/src/endpoints/levels/uploadLevel.cr index 4734131..4fbca5b 100644 --- a/src/endpoints/levels/uploadLevel.cr +++ b/src/endpoints/levels/uploadLevel.cr @@ -50,7 +50,7 @@ CrystalGauntlet.endpoints["/uploadGJLevel21.php"] = ->(body : String): String { raise "not implemented" else # create new level - next_id = (DATABASE.scalar("select max(id) from levels").as(Int64 | Nil) || 0) + 1 + next_id = IDs.get_next_id("levels") DATABASE.exec("insert into levels (id, name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, level_data, extra_data, level_info, wt1, wt2, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", next_id, Clean.clean_special(params["levelName"]), user_id, description, params["original"].to_i32, params["gameVersion"].to_i32, params["binaryVersion"].to_i32, params["password"] == "0" ? nil : params["password"].to_i32, params["requestedStars"].to_i32, params["unlisted"].to_i32, params["levelVersion"].to_i32, Clean.clean_b64(params["levelString"]), Clean.clean_special(extraString), Clean.clean_b64(params["levelInfo"]), Clean.clean_number(params["wt"]), Clean.clean_number(params["wt2"]), song_id.to_i32, params["levelLength"].to_i32, params["objects"].to_i32, params["coins"].to_i32, params["ldm"].to_i32, params["twoPlayer"].to_i32) diff --git a/src/endpoints/users/addProfileComment.cr b/src/endpoints/users/addProfileComment.cr index 2551aba..9f8e212 100644 --- a/src/endpoints/users/addProfileComment.cr +++ b/src/endpoints/users/addProfileComment.cr @@ -16,7 +16,7 @@ CrystalGauntlet.endpoints["/uploadGJAccComment20.php"] = ->(body : String): Stri if comment && comment != "" # todo: cap comment size comment_value = Base64.decode_string comment # usual b64, surprisingly - next_id = (DATABASE.scalar("select max(id) from account_comments").as(Int64 | Nil) || 0) + 1 + next_id = IDs.get_next_id("account_comments") DATABASE.exec("insert into account_comments (id, account_id, comment) values (?, ?, ?)", next_id, account_id, comment_value) return "1" else diff --git a/src/lib/ids.cr b/src/lib/ids.cr new file mode 100644 index 0000000..7eb4340 --- /dev/null +++ b/src/lib/ids.cr @@ -0,0 +1,19 @@ +include CrystalGauntlet + +module CrystalGauntlet::IDs + extend self + + def get_next_id(key : String) : Int32 + begin + id = DATABASE.query_one("select id from next_id where name = ?", key, as: {Int32}) + rescue + next_id = 1 + DATABASE.exec("insert into next_id (name, id) values (?, ?)", key, next_id) + next_id + else + next_id = id + 1 + DATABASE.exec("update next_id set id = ?", next_id) + return next_id + end + end +end diff --git a/src/lib/songs.cr b/src/lib/songs.cr index 8c17b04..6baee63 100644 --- a/src/lib/songs.cr +++ b/src/lib/songs.cr @@ -102,7 +102,7 @@ module CrystalGauntlet::Songs begin DATABASE.query_one("select id from song_authors where name = ? and url = ? and source = ?", artist_name, artist_url, source, as: {Int32}) rescue - next_id = (DATABASE.scalar("select max(id) from song_authors").as(Int64 | Nil) || 0) + 1 + next_id = IDs.get_next_id("song_authors") DATABASE.exec("insert into song_authors (id, source, name, url) values (?, ?, ?, ?)", next_id, source, artist_name, artist_url) next_id.to_i end