diff --git a/Cargo.lock b/Cargo.lock index 90221a8..4d3039f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + [[package]] name = "cpufeatures" version = "0.2.9" @@ -399,6 +415,30 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -485,9 +525,12 @@ dependencies = [ "password-auth", "rand", "regex", + "reqwest", "rocket", "rocket_dyn_templates", + "roxmltree", "serde", + "serde_json", "sha", "toml", ] @@ -643,6 +686,29 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -690,6 +756,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + [[package]] name = "is-terminal" version = "0.4.9" @@ -853,6 +925,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "normpath" version = "1.1.1" @@ -906,6 +996,50 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -1044,6 +1178,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1193,6 +1333,43 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rocket" version = "0.5.0-rc.3" @@ -1287,6 +1464,15 @@ dependencies = [ "uncased", ] +[[package]] +name = "roxmltree" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" +dependencies = [ + "xmlparser", +] + [[package]] name = "rustix" version = "0.38.9" @@ -1321,6 +1507,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1333,6 +1528,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.188" @@ -1355,9 +1573,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1373,6 +1591,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha" version = "1.0.3" @@ -1548,6 +1778,21 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.27.0" @@ -1577,6 +1822,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.12" @@ -1741,18 +1996,44 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "valuable" version = "0.1.0" @@ -1821,6 +2102,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -1850,6 +2143,16 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2031,6 +2334,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index d7b232b..5161ef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,11 @@ maplit = "1.0.2" password-auth = "0.3.0" rand = "0.8.5" regex = "1.9.4" +reqwest = { version = "0.11.20", features = ["blocking"] } rocket = "=0.5.0-rc.3" rocket_dyn_templates = { version = "0.1.0-rc.3", features = ["handlebars"] } +roxmltree = "0.18.0" serde = { version = "1.0.188", features = ["derive"] } +serde_json = "1.0.107" sha = "1.0.3" toml = "0.7.6" diff --git a/config.example.toml b/config.example.toml index d448962..16a518f 100644 --- a/config.example.toml +++ b/config.example.toml @@ -28,4 +28,6 @@ data_folder = "data" # max amount of objects in a level max_objects = 80_000 # object ids to block -blocklist = [ 31 ] # start position \ No newline at end of file +blocklist = [ 31 ] # start position +# if reuploading levels is allowed +reupload = true \ No newline at end of file diff --git a/migrations/2023-09-03-032651_levels/up.sql b/migrations/2023-09-03-032651_levels/up.sql index 3b53884..531b2b3 100644 --- a/migrations/2023-09-03-032651_levels/up.sql +++ b/migrations/2023-09-03-032651_levels/up.sql @@ -24,12 +24,11 @@ CREATE TABLE levels ( song_id INTEGER NOT NULL, - length INTEGER NOT NULL, - length_real DOUBLE PRECISION NOT NULL, - objects INTEGER NOT NULL, - coins INTEGER NOT NULL DEFAULT 0, - has_ldm INTEGER NOT NULL DEFAULT 0, - two_player INTEGER NOT NULL DEFAULT 0, + length INTEGER NOT NULL, + objects INTEGER NOT NULL, + coins INTEGER NOT NULL DEFAULT 0, + has_ldm INTEGER NOT NULL DEFAULT 0, + two_player INTEGER NOT NULL DEFAULT 0, downloads INTEGER NOT NULL DEFAULT 0, likes INTEGER NOT NULL DEFAULT 0, diff --git a/public/style.css b/public/style.css index a2b7024..f2dfc52 100644 --- a/public/style.css +++ b/public/style.css @@ -9,6 +9,8 @@ line-height: 1; font-weight: 400; + color-scheme: light dark; + text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/readme.md b/readme.md index 3bd4777..83cb720 100644 --- a/readme.md +++ b/readme.md @@ -34,7 +34,6 @@ _these features are implemented_ ## todo -- __we're doing song uploading/rework and level reuploading next__ - swap to chrono instead of `(TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS'))` (thats REALLY ugly!!) this would also make the `28` (upload) and `29` (update) responses work on downloadlevel and the `4` (recent) on getlevels - 2.2 friends only unlisted - add more old endpoints + better support for older versions @@ -43,4 +42,9 @@ _these features are implemented_ - moderation utilities - ip actions - better song support -- return "-1" instead of panicking for stuff \ No newline at end of file +- return "-1" instead of panicking for stuff +- authentication caching (ip? redis?) +- idfk where to put this but i need to rant about this. why cant you have `get` and `post` in the same function for rocket. like??? why?? +- use log instead of println +- find what the fuck level info is. gddocs just says "a random gzip string" like bro what +- unscuff parsing \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 8af07f6..4b73286 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,7 @@ use serde::Deserialize; + use std::fs; + use std::sync::LazyLock; #[derive(Deserialize)] @@ -30,7 +32,8 @@ pub struct ConfigDB { #[derive(Deserialize)] pub struct ConfigLevels { pub max_objects: i32, - pub blocklist: Vec + pub blocklist: Vec, + pub reupload: bool } impl Config { diff --git a/src/db/models.rs b/src/db/models.rs index 55a12b9..7e6497f 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -108,7 +108,6 @@ pub struct Level { pub editor_time_copies: i32, pub song_id: i32, pub length: i32, - pub length_real: f64, pub objects: i32, pub coins: i32, pub has_ldm: i32, @@ -143,7 +142,6 @@ pub struct NewLevel { pub editor_time_copies: i32, pub song_id: i32, pub length: i32, - pub length_real: f64, pub objects: i32, pub coins: i32, pub has_ldm: i32, diff --git a/src/db/schema.rs b/src/db/schema.rs index f116654..1a25502 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -46,7 +46,6 @@ diesel::table! { editor_time_copies -> Int4, song_id -> Int4, length -> Int4, - length_real -> Float8, objects -> Int4, coins -> Int4, has_ldm -> Int4, diff --git a/src/endpoints/levels/download_level.rs b/src/endpoints/levels/download_level.rs index ebcc15b..bc8e692 100644 --- a/src/endpoints/levels/download_level.rs +++ b/src/endpoints/levels/download_level.rs @@ -93,7 +93,7 @@ pub fn download_level(input: Form) -> status::Custom<&'static let xor_pass: String; if input.gameVersion.unwrap_or(19) >= 20 { - xor_pass = helpers::encryption::cyclic_xor_string(&level.password.clone().unwrap_or(String::from("0")), "26364") + xor_pass = general_purpose::URL_SAFE.encode(helpers::encryption::cyclic_xor_string(&level.password.clone().unwrap_or(String::from("0")), "26364")) } else { xor_pass = level.password.clone().unwrap_or(String::from("0")); } diff --git a/src/endpoints/levels/get_levels.rs b/src/endpoints/levels/get_levels.rs index 00602be..714c728 100644 --- a/src/endpoints/levels/get_levels.rs +++ b/src/endpoints/levels/get_levels.rs @@ -45,7 +45,6 @@ pub fn get_levels(input: Form) -> status::Custom<&'static str> { let connection = &mut db::establish_connection_pg(); use crate::schema::{levels, users}; - use crate::models::{Level, User}; let mut can_see_unlisted = false; diff --git a/src/endpoints/levels/upload_level.rs b/src/endpoints/levels/upload_level.rs index 2ebdcba..ece7568 100644 --- a/src/endpoints/levels/upload_level.rs +++ b/src/endpoints/levels/upload_level.rs @@ -183,7 +183,6 @@ pub fn upload_level(input: Form) -> status::Custom<&'static str editor_time_copies: input.wt2.unwrap_or(0), song_id: song_id_val, length: level_length_val, - length_real: level_length_secs, objects: objects_val as i32, coins: coins_val as i32, has_ldm: input.ldm.unwrap_or(0).clamp(0, 1), diff --git a/src/helpers.rs b/src/helpers.rs index d583aa1..1c3391f 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -3,4 +3,5 @@ pub mod clean; pub mod difficulty; pub mod encryption; pub mod format; -pub mod levels; \ No newline at end of file +pub mod levels; +pub mod reupload; \ No newline at end of file diff --git a/src/helpers/levels.rs b/src/helpers/levels.rs index 5cf3777..acce802 100644 --- a/src/helpers/levels.rs +++ b/src/helpers/levels.rs @@ -5,6 +5,8 @@ use base64::{Engine as _, engine::general_purpose}; use flate2::read::GzDecoder; +use roxmltree::Document; + use std::collections::HashMap; pub static DEFAULT_EXTRA_STRING: LazyLock = LazyLock::new(|| { @@ -13,6 +15,12 @@ pub static DEFAULT_EXTRA_STRING: LazyLock = LazyLock::new(|| { return string; }); +pub static DEFAULT_LEVEL_INFO: LazyLock = LazyLock::new(|| { + let string = String::from(""); + + return string; +}); + macro_rules! object_prop_bool { ($key:expr, $name:ident) => { pub fn $name(&self) -> bool { @@ -167,6 +175,21 @@ pub fn parse(raw_level_data: &str) -> Vec> { .collect() } +pub fn gmd_parse(gmd_file: &str) -> HashMap { + let doc = Document::parse(gmd_file).expect("failed to parse gmd file"); + let root = doc.root_element(); + + let mut result = Vec::new(); + + for child in root.children().filter(|node| node.node_type() != roxmltree::NodeType::Text) { + if let Some(child_text) = child.children().next() { + result.push(child_text.text().unwrap_or("").to_string()); + } + } + + return array_to_hash(result); +} + pub fn decode(level_data: String) -> Vec> { let decoded_bytes = general_purpose::URL_SAFE.decode(level_data).expect("couldnt decode b64"); diff --git a/src/helpers/reupload.rs b/src/helpers/reupload.rs new file mode 100644 index 0000000..67ffc68 --- /dev/null +++ b/src/helpers/reupload.rs @@ -0,0 +1,59 @@ +use std::sync::RwLock; + +use diesel::prelude::*; + +use crate::db; + +pub const REUPLOAD_USER_NAME: &str = "reupload"; + +pub static REUPLOAD_ACCOUNT_ID: RwLock = RwLock::new(0); + +pub fn init() { + let connection = &mut db::establish_connection_pg(); + + use crate::schema::{accounts, users}; + use crate::models::{Account, NewAccount, User, NewUser}; + + match accounts::table + .filter(accounts::username.eq(REUPLOAD_USER_NAME)) + .select(accounts::id) + .get_result::(connection) { + Ok(reupload_acc_id) => { + let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock!!"); + *write_lock = reupload_acc_id; + + println!("reupload account found, id: {}", reupload_acc_id); + }, + Err(_) => { + let new_account = NewAccount { + username: REUPLOAD_USER_NAME.to_string(), + gjp2: "!".to_string(), + password: "!".to_string(), + email: "".to_string() + }; + + let inserted_account = diesel::insert_into(accounts::table) + .values(&new_account) + .get_result::(connection) + .expect("Fatal error saving the new account"); + + let reupload_acc_id = inserted_account.id; + + let new_user = NewUser { + account_id: inserted_account.id, + username: REUPLOAD_USER_NAME.to_string(), + registered: 1 + }; + + diesel::insert_into(users::table) + .values(&new_user) + .get_result::(connection) + .expect("Fatal error saving the new user"); + + let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock!!"); + *write_lock = reupload_acc_id; + + println!("created reupload account, id: {}", reupload_acc_id); + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cd1b439..f8abf9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,10 @@ async fn files(file: PathBuf) -> Option { #[launch] fn rocket() -> _ { + // init stuff + crate::helpers::reupload::init(); + + // data directories // this is a bit scuffed fs::create_dir_all(&CONFIG.db.data_folder).expect("failed to create data directory!"); fs::create_dir_all(format!("{}/levels", &CONFIG.db.data_folder)).expect("failed to create data directory for levels"); @@ -46,13 +50,16 @@ fn rocket() -> _ { .merge(("limits", Limits::new().limit("forms", 10.megabytes())))) // actual website .mount("/", routes![ - template_endpoints::index::index + template_endpoints::index::index, + + template_endpoints::reupload::post_reupload, + template_endpoints::reupload::get_reupload ]) // assets .mount("/", routes![ files ]) - // GEOMETRY DASH https://www.youtube.com/watch?v=_pLrtsf5yfE + // https://www.youtube.com/watch?v=_pLrtsf5yfE .mount(CONFIG.general.append_path.as_str(), routes![ endpoints::accounts::login_account::login_account, endpoints::accounts::register_account::register_account, diff --git a/src/template_endpoints.rs b/src/template_endpoints.rs index 5d4ca1d..50567bf 100644 --- a/src/template_endpoints.rs +++ b/src/template_endpoints.rs @@ -1 +1,2 @@ -pub mod index; \ No newline at end of file +pub mod index; +pub mod reupload; \ No newline at end of file diff --git a/src/template_endpoints/reupload.rs b/src/template_endpoints/reupload.rs new file mode 100644 index 0000000..c46b8b7 --- /dev/null +++ b/src/template_endpoints/reupload.rs @@ -0,0 +1,130 @@ +use rocket_dyn_templates::{Template, context}; + +use rocket::form::Form; + +use reqwest; + +use serde::Deserialize; + +use serde_json; + +use std::fs; + +use base64::{Engine as _, engine::general_purpose}; + +use diesel::prelude::*; + +use crate::helpers; +use crate::db; + +#[derive(Deserialize)] +struct LevelResults { + records: Vec +} + +#[derive(Deserialize, Debug)] +struct LevelRecord { + level_string_available: bool, + real_date: String, + length: Option, + id: i32 +} + +#[derive(FromForm)] +pub struct FormReupload { + level_id: i32 +} + +#[post("/tools/reupload", data = "")] +pub async fn post_reupload(input: Form) -> Template { + let connection = &mut db::establish_connection_pg(); + + let disabled = !crate::CONFIG.levels.reupload; + + let error: Option = None; + if !disabled { + let remote_level_id = input.level_id; + + let resp = reqwest::get(format!("https://history.geometrydash.eu/api/v1/level/{}", remote_level_id)).await.expect("failed to fetch level from remote server"); + if !resp.status().is_success() { + return Template::render("reupload", context! { + error: Some(format!("Recieved status code: {}", resp.status())) + }) + } + + let text = resp.text().await.expect("failed to parse response as text"); + let data: LevelResults = serde_json::from_str(&text).expect("failed to parse response as json"); + + let level: LevelRecord = match data.records + .into_iter() + .filter(|record| record.level_string_available) + .max_by_key(|record| record.real_date.clone()) + .map(|record| record) { + Some(level) => level, + None => { + return Template::render("reupload", context! { + error: Some(String::from("No level string available")) + }) + } + }; + + let gmd_file = reqwest::get(format!("https://history.geometrydash.eu/level/{}/{}/download/", remote_level_id, level.id)).await.expect("failed to fetch gmd file from remote server"); + let level_data = helpers::levels::gmd_parse(&gmd_file.text().await.expect("failed to parse gmd file as text")); + + use crate::schema::levels::dsl::*; + use crate::models::{Level, NewLevel}; + + println!("{:?}", level_data.get("k3")); + + let new_level = NewLevel { + name: level_data.get("k2").expect("level name not found").to_string(), + user_id: crate::helpers::reupload::REUPLOAD_ACCOUNT_ID.read().expect("poisoned lock!!").to_string().parse::().expect("reupload account id not int (shouldnt ever happen)"), + description: String::from_utf8(general_purpose::URL_SAFE.decode(general_purpose::URL_SAFE.decode(level_data.get("k3").expect("level description not found")).expect("couldnt decode base64")).expect("couldnt decode base64")).expect("invalid utf-8 sequence (how)"), + original: None, + game_version: level_data.get("k17").expect("level game version not found").to_string().parse::().expect("level game version not int"), + binary_version: level_data.get("k50").unwrap_or(&String::from("0")).to_string().parse::().expect("level binary version not int"), + password: Some(level_data.get("k41").expect("level password not found").to_string()), + requested_stars: level_data.get("k66").expect("level requested stars not found").to_string().parse::().expect("level requested stars not int"), + unlisted: 0, + version: level_data.get("k16").expect("level version not found").to_string().parse::().expect("level version not int"), + extra_data: level_data.get("extra_string").unwrap_or(&crate::helpers::levels::DEFAULT_EXTRA_STRING).to_string().into_bytes(), + level_info: crate::helpers::levels::DEFAULT_LEVEL_INFO.to_string().into_bytes(), + editor_time: level_data.get("k80").unwrap_or(&String::from("0")).parse::().expect("level editor time not int"), + editor_time_copies: level_data.get("k81").unwrap_or(&String::from("0")).parse::().expect("level editor time copies not int"), + song_id: if level_data.get("k8").unwrap_or(&String::from("0")).parse::().expect("level song id not int") == 0 { + level_data.get("k45").expect("level song id doesnt fucking exist").parse::().expect("level song id not int") + } else { + level_data.get("k8").expect("level song id doesnt fucking exist").parse::().expect("level song id not int") + }, + length: level.length.expect("level length doesnt fucking exist"), + objects: level_data.get("k48").expect("level object count doesnt exist").parse::().expect("object count not int"), + coins: level_data.get("k64").unwrap_or(&String::from("0")).parse::().expect("coins not int"), + has_ldm: level_data.get("k72").unwrap_or(&String::from("0")).parse::().expect("ldm not int"), + two_player: level_data.get("k43").unwrap_or(&String::from("0")).parse::().expect("two player not int") + }; + + let inserted_level = diesel::insert_into(levels) + .values(&new_level) + .get_result::(connection) + .expect("failed to insert level"); + + fs::write(format!("{}/levels/{}.lvl", crate::CONFIG.db.data_folder, inserted_level.id), general_purpose::URL_SAFE.decode(level_data.get("k4").expect("no level data?!").as_bytes()).expect("user provided invalid level string")).expect("couldnt write level to file"); + + return Template::render("reupload", context! { + level_id: inserted_level.id + }) + } + + Template::render("reupload", context! { + disabled: disabled + }) +} + +#[get("/tools/reupload")] +pub fn get_reupload() -> Template { + let disabled = !crate::CONFIG.levels.reupload; + + Template::render("reupload", context! { + disabled: disabled + }) +} \ No newline at end of file diff --git a/templates/index.html.hbs b/templates/index.html.hbs index ee2b5c4..612426d 100644 --- a/templates/index.html.hbs +++ b/templates/index.html.hbs @@ -23,6 +23,7 @@
  • The Git repository
  • +
  • Level reuploading
  • diff --git a/templates/reupload.html.hbs b/templates/reupload.html.hbs new file mode 100644 index 0000000..98da58d --- /dev/null +++ b/templates/reupload.html.hbs @@ -0,0 +1,33 @@ + + + + + + + + + Level Reupload + + + +

    Level Reupload

    + + {{#if error}} +
    Error while uploading: {{ error }}

    + {{/if}} + {{#if level_id}} +
    Uploaded successfully! Level ID: {{ level_id }}

    + {{/if}} + {{#if disabled}} + Reuploading levels has been disabled.

    + {{/if}} + +
    + ID: + +
    +
    + Only vanilla servers are supported. + + + \ No newline at end of file