switch all auto-incrementing ids to a single tracking system

this is to prevent ids from overlapping after a level is deleted

this WILL cause issues on older databases. i will not be responsible
This commit is contained in:
Jill 2023-01-02 16:32:31 +03:00
parent 02461ec944
commit d4e930e424
10 changed files with 44 additions and 13 deletions

View File

@ -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;

View File

@ -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 }

View File

@ -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"
}

View File

@ -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

View File

@ -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("#")

View File

@ -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
}

View File

@ -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)

View File

@ -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

19
src/lib/ids.cr Normal file
View File

@ -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

View File

@ -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