diff --git a/Cargo.lock b/Cargo.lock index aedb028..98f6cc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -149,6 +149,34 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "based" +version = "0.1.0" +source = "git+https://git.hydrar.de/jmarya/based#98048ce522db134fe3e863538db6793068085b80" +dependencies = [ + "bcrypt 0.16.0", + "chrono", + "dashmap", + "data-encoding", + "env_logger 0.10.2", + "futures", + "hex", + "log", + "maud", + "rand", + "rayon", + "regex", + "ring", + "rocket", + "rocket_cors", + "serde", + "serde_json", + "sqlx", + "tokio", + "uuid", + "walkdir", +] + [[package]] name = "bcrypt" version = "0.15.1" @@ -162,6 +190,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bcrypt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e" +dependencies = [ + "base64", + "blowfish", + "getrandom", + "subtle", + "zeroize", +] + [[package]] name = "binascii" version = "0.1.4" @@ -222,9 +263,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "shlex", ] @@ -237,9 +278,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -327,19 +368,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] -name = "crossbeam-queue" -version = "0.3.11" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -351,6 +411,20 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -466,6 +540,19 @@ dependencies = [ "log", ] +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.11.5" @@ -516,9 +603,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "figment" @@ -542,7 +629,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -583,6 +670,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -633,6 +721,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -654,6 +753,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -796,11 +896,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -849,10 +949,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "hyper" -version = "0.14.31" +name = "humantime" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -1079,9 +1185,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", @@ -1093,14 +1199,14 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] name = "libc" -version = "0.2.167" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libm" @@ -1171,6 +1277,28 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "maud" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df518b75016b4289cdddffa1b01f2122f4a49802c93191f3133f6dc2472ebcaa" +dependencies = [ + "itoa", + "maud_macros", +] + +[[package]] +name = "maud_macros" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa453238ec218da0af6b11fc5978d3b5c3a45ed97b722391a2a11f3306274e18" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1201,9 +1329,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -1232,7 +1360,7 @@ dependencies = [ "httparse", "memchr", "mime", - "spin", + "spin 0.9.8", "tokio", "tokio-util", "version_check", @@ -1530,6 +1658,29 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -1592,10 +1743,30 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.7" +name = "rayon" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags", ] @@ -1664,6 +1835,21 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rocket" version = "0.5.1" @@ -1791,15 +1977,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1859,9 +2045,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -1869,18 +2055,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -2004,6 +2190,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -2303,10 +2495,11 @@ dependencies = [ name = "synthwave" version = "0.1.0" dependencies = [ - "bcrypt", + "based", + "bcrypt 0.15.1", "chrono", "data-encoding", - "env_logger", + "env_logger 0.11.5", "log", "rand", "rocket", @@ -2331,6 +2524,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2427,6 +2629,7 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2620,9 +2823,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" @@ -2657,6 +2860,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.5.4" @@ -2741,9 +2950,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -2752,13 +2961,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -2767,9 +2975,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2777,9 +2985,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -2790,9 +2998,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "whoami" diff --git a/Cargo.toml b/Cargo.toml index 0219701..13cab4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ log = "0.4.22" chrono = { version = "0.4.38", features = ["serde"] } uuid = { version = "1.8.0", features = ["v4", "serde"] } sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-native-tls", "derive", "uuid", "chrono", "json"] } +based = { git = "https://git.hydrar.de/jmarya/based", features = ["cache"] } \ No newline at end of file diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index 8439f04..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::collections::HashMap; - -use rocket::tokio::sync::RwLock; - -#[macro_export] -macro_rules! use_api_cache { - ($route:literal, $id:ident, $cache:ident) => { - if let Some(ret) = $cache.get_only($route, $id).await { - return Ok(serde_json::from_str(&ret).unwrap()); - } - }; - ($route:literal, $id:literal, $cache:ident) => { - if let Some(ret) = $cache.get_only($route, $id).await { - return Ok(serde_json::from_str(&ret).unwrap()); - } - }; -} - -pub struct RouteCache { - inner: RwLock>>>, -} - -impl RouteCache { - pub fn new() -> Self { - Self { - inner: RwLock::new(HashMap::new()), - } - } - - pub async fn get(&self, route: &str, id: &str, generator: F) -> String - where - F: FnOnce() -> Fut, - Fut: std::future::Future, - { - { - // Try to get a read lock first. - let lock = self.inner.read().await; - if let Some(inner_map) = lock.get(route) { - if let Some(cached_value) = inner_map.get(id) { - log::info!("Using cached value for {route} / {id}"); - return cached_value.clone().unwrap(); - } - } - } - - // If the value was not found, acquire a write lock to insert the computed value. - let mut lock = self.inner.write().await; - - log::info!("Computing value for {route} / {id}"); - let computed = generator().await; - - lock.entry(route.to_string()) - .or_insert_with(HashMap::new) - .insert(id.to_string(), Some(computed.clone())); - - computed - } - - pub async fn get_only(&self, route: &str, id: &str) -> Option { - let lock = self.inner.read().await; - if let Some(inner_map) = lock.get(route) { - if let Some(cached_value) = inner_map.get(id) { - log::info!("Using cached value for {route} / {id}"); - return cached_value.clone(); - } - } - - None - } - - pub async fn get_option(&self, route: &str, id: &str, generator: F) -> Option - where - F: FnOnce() -> Fut, - Fut: std::future::Future>, - { - { - // Try to get a read lock first. - let lock = self.inner.read().await; - if let Some(inner_map) = lock.get(route) { - if let Some(cached_value) = inner_map.get(id) { - log::info!("Using cached value for {route} / {id}"); - return cached_value.clone(); - } - } - } - - // If the value was not found, acquire a write lock to insert the computed value. - let mut lock = self.inner.write().await; - - log::info!("Computing value for {route} / {id}"); - let computed = generator().await; - - lock.entry(route.to_string()) - .or_insert_with(HashMap::new) - .insert(id.to_string(), computed.clone()); - - computed - } - - pub async fn insert(&self, route: &str, id: &str, value: String) { - let mut lock = self.inner.write().await; - - log::info!("Inserting value for {route} / {id}"); - - lock.entry(route.to_string()) - .or_insert_with(HashMap::new) - .insert(id.to_string(), Some(value)); - } - - pub async fn invalidate(&self, route: &str, id: &str) { - let mut lock = self.inner.write().await; - if let Some(inner_map) = lock.get_mut(route) { - inner_map.remove(id); - - // If the inner map is empty, remove the route entry as well. - if inner_map.is_empty() { - lock.remove(route); - } - } - } -} diff --git a/src/library/album.rs b/src/library/album.rs index 67d7629..f1b80dc 100644 --- a/src/library/album.rs +++ b/src/library/album.rs @@ -1,4 +1,4 @@ -use crate::route::ToAPI; +use based::request::api::ToAPI; use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::FromRow; diff --git a/src/library/artist.rs b/src/library/artist.rs index 3024466..67f9b93 100644 --- a/src/library/artist.rs +++ b/src/library/artist.rs @@ -1,4 +1,4 @@ -use crate::route::ToAPI; +use based::request::api::ToAPI; use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::FromRow; diff --git a/src/library/event.rs b/src/library/event.rs index e7fc37d..51c1d2f 100644 --- a/src/library/event.rs +++ b/src/library/event.rs @@ -1,7 +1,7 @@ +use based::{auth::User, get_pg}; use serde::{Deserialize, Serialize}; use sqlx::prelude::FromRow; -use crate::{get_pg, library::user::User}; /// Represents a user event in the database. #[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Event { diff --git a/src/library/mod.rs b/src/library/mod.rs index 8969257..8b18db6 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,16 +1,13 @@ +use album::Album; +use artist::Artist; +use based::{get_pg, request::cache::RouteCache}; +use serde_json::json; use std::{ path::{Path, PathBuf}, str::FromStr, }; - -use album::Album; -use artist::Artist; -use serde_json::json; use track::Track; use walkdir::WalkDir; - -use crate::{cache::RouteCache, get_pg}; - pub mod album; pub mod artist; pub mod event; @@ -18,7 +15,6 @@ pub mod metadata; pub mod playlist; pub mod search; pub mod track; -pub mod user; /// Checks if a file has a music file extension fn is_music_file(path: &Path) -> bool { diff --git a/src/library/playlist.rs b/src/library/playlist.rs index 468c26c..b12a6df 100644 --- a/src/library/playlist.rs +++ b/src/library/playlist.rs @@ -1,8 +1,7 @@ +use based::{auth::User, get_pg}; use serde::{Deserialize, Serialize}; use sqlx::FromRow; -use crate::{get_pg, library::user::User}; - #[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Playlist { // Unique identifier of the playlist diff --git a/src/library/search.rs b/src/library/search.rs index 36a0b2b..f100a2c 100644 --- a/src/library/search.rs +++ b/src/library/search.rs @@ -1,10 +1,8 @@ +use super::{album::Album, artist::Artist, playlist::Playlist, track::Track}; +use based::request::api::ToAPI; use serde::Serialize; use std::cmp::Ordering; -use crate::route::ToAPI; - -use super::{album::Album, artist::Artist, playlist::Playlist, track::Track}; - // Calculate a score for a given field based on its similarity to the search term and recency factor fn calculate_score(field: &str, search_term: &str, date_added: Option) -> f64 { // Exact match bonus: assign a high score if the field exactly matches the search term diff --git a/src/library/track.rs b/src/library/track.rs index c2211f2..3233754 100644 --- a/src/library/track.rs +++ b/src/library/track.rs @@ -1,15 +1,15 @@ +use crate::library::album::Album; +use based::{ + auth::User, + get_pg, + request::api::{to_uuid, ToAPI}, +}; use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::prelude::FromRow; use std::{io::Read, str::FromStr}; -use crate::{ - get_pg, - library::album::Album, - route::{to_uuid, ToAPI}, -}; - -use super::{metadata::AudioMetadata, user::User}; +use super::metadata::AudioMetadata; #[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Track { diff --git a/src/library/user.rs b/src/library/user.rs deleted file mode 100644 index 4931c16..0000000 --- a/src/library/user.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::route::ToAPI; -use data_encoding::HEXUPPER; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use serde_json::json; -use sqlx::FromRow; - -use crate::get_pg; - -fn gen_token(token_length: usize) -> String { - let mut token_bytes = vec![0u8; token_length]; - - rand::thread_rng().fill_bytes(&mut token_bytes); - - HEXUPPER.encode(&token_bytes) -} - -#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] -pub struct User { - /// The username chosen by the user - pub username: String, - /// The hashed password for the user - pub password: String, - /// The role of the user - pub user_role: UserRole, -} - -#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)] -#[sqlx(type_name = "user_role", rename_all = "lowercase")] -pub enum UserRole { - /// A regular user with limited permissions - Regular, - /// An admin user with full system privileges - Admin, -} - -impl User { - /// Find a user by their username - pub async fn find(username: &str) -> Option { - sqlx::query_as("SELECT * FROM users WHERE username = $1") - .bind(username) - .fetch_optional(get_pg!()) - .await - .unwrap() - } - - /// Create a new user with the given details - /// - /// Returns an Option containing the created user, or None if a user already exists with the same username - pub async fn create(username: &str, password: &str, role: UserRole) -> Option { - // Check if a user already exists with the same username - if Self::find(username).await.is_some() { - return None; - } - - let u = Self { - username: username.to_string(), - password: bcrypt::hash(password, bcrypt::DEFAULT_COST).unwrap(), - user_role: role, - }; - - sqlx::query("INSERT INTO users (username, \"password\", user_role) VALUES ($1, $2, $3)") - .bind(&u.username) - .bind(&u.password) - .bind(&u.user_role) - .execute(get_pg!()) - .await - .unwrap(); - - Some(u) - } - - /// Login a user with the given username and password - pub async fn login(username: &str, password: &str) -> Option<(Session, UserRole)> { - let u = Self::find(username).await?; - - if !u.verify_pw(password) { - return None; - } - - Some((u.session().await, u.user_role)) - } - - /// Change the password of a User - /// - /// Returns a Result indicating whether the password change was successful or not - pub async fn passwd(self, old: &str, new: &str) -> Result<(), ()> { - if self.verify_pw(old) { - sqlx::query("UPDATE users SET \"password\" = $1 WHERE username = $2;") - .bind(bcrypt::hash(new, bcrypt::DEFAULT_COST).unwrap()) - .bind(&self.username) - .fetch_one(get_pg!()) - .await - .unwrap(); - - return Ok(()); - } - - Err(()) - } - - /// Find all users in the system - pub async fn find_all() -> Vec { - sqlx::query_as("SELECT * FROM users") - .fetch_all(get_pg!()) - .await - .unwrap() - } - - /// Generate a new session token for the user - /// - /// Returns a Session instance containing the generated token and associated user - pub async fn session(&self) -> Session { - sqlx::query_as( - "INSERT INTO user_session (token, \"user\") VALUES ($1, $2) RETURNING id, token, \"user\"", - ) - .bind(gen_token(64)) - .bind(&self.username) - .fetch_one(get_pg!()) - .await - .unwrap() - } - - /// Check if the user is an admin - pub const fn is_admin(&self) -> bool { - matches!(self.user_role, UserRole::Admin) - } - - /// Verify that a provided password matches the hashed password for the user - /// - /// Returns a boolean indicating whether the passwords match or not - pub fn verify_pw(&self, password: &str) -> bool { - bcrypt::verify(password, &self.password).unwrap() - } -} - -impl ToAPI for User { - async fn api(&self) -> serde_json::Value { - json!({ - "username": self.username, - "role": self.user_role - }) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] -pub struct Session { - /// The unique ID of the session token - pub id: uuid::Uuid, - /// The generated session token - pub token: String, - /// The username associated with the session token - pub user: String, -} diff --git a/src/main.rs b/src/main.rs index b4af49c..555a2fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,13 @@ +use based::auth::{User, UserRole}; +use based::get_pg; +use based::request::cache; use library::Libary; - -mod cache; -mod library; -mod route; - -use library::user::{User, UserRole}; use rocket::routes; use rocket::tokio::sync::OnceCell; use rocket::{http::Method, launch}; -pub static PG: OnceCell = OnceCell::const_new(); - -#[macro_export] -macro_rules! get_pg { - () => { - if let Some(client) = $crate::PG.get() { - client - } else { - let client = sqlx::postgres::PgPoolOptions::new() - .max_connections(5) - .connect(&std::env::var("DATABASE_URL").unwrap()) - .await - .unwrap(); - $crate::PG.set(client).unwrap(); - $crate::PG.get().unwrap() - } - }; -} +mod library; +mod route; #[launch] async fn rocket() -> _ { diff --git a/src/route/admin.rs b/src/route/admin.rs index 2c1f92f..5413b66 100644 --- a/src/route/admin.rs +++ b/src/route/admin.rs @@ -1,15 +1,11 @@ -use super::api_error; -use super::FallibleApiResponse; -use crate::get_pg; -use crate::route::vec_to_api; +use crate::library::track::Track; +use crate::library::Libary; +use based::auth::User; +use based::request::api::{vec_to_api, FallibleApiResponse}; +use based::{check_admin, get_pg}; use rocket::{get, State}; use serde_json::json; -use crate::check_admin; -use crate::library::track::Track; -use crate::library::user::User; -use crate::library::Libary; - #[get("/library/clean")] pub async fn clean_library(lib: &State, u: User) -> FallibleApiResponse { check_admin!(u); diff --git a/src/route/album.rs b/src/route/album.rs index 633e42d..243d8c8 100644 --- a/src/route/album.rs +++ b/src/route/album.rs @@ -1,19 +1,13 @@ -use std::cmp::Ordering; - -use super::api_error; -use super::to_uuid; -use super::FallibleApiResponse; -use rocket::fs::NamedFile; -use rocket::{get, State}; -use serde_json::json; - use crate::cache::RouteCache; use crate::library::album::Album; use crate::library::track::Track; use crate::library::Libary; -use crate::route::vec_to_api; -use crate::route::ToAPI; -use crate::use_api_cache; +use based::request::api::{api_error, to_uuid, vec_to_api, FallibleApiResponse, ToAPI}; +use based::use_api_cache; +use rocket::fs::NamedFile; +use rocket::{get, State}; +use serde_json::json; +use std::cmp::Ordering; #[get("/artist//albums")] pub async fn albums_route(artist_id: &str, lib: &State) -> FallibleApiResponse { diff --git a/src/route/artist.rs b/src/route/artist.rs index 02b8b73..9674957 100644 --- a/src/route/artist.rs +++ b/src/route/artist.rs @@ -1,8 +1,4 @@ -use super::api_error; -use super::to_uuid; -use super::vec_to_api; -use super::FallibleApiResponse; -use super::ToAPI; +use based::request::api::{api_error, to_uuid, vec_to_api, FallibleApiResponse, ToAPI}; use fs::NamedFile; use rocket::{fs, get, State}; diff --git a/src/route/event.rs b/src/route/event.rs index 5b48396..5be9690 100644 --- a/src/route/event.rs +++ b/src/route/event.rs @@ -1,16 +1,15 @@ -use super::api_error; -use super::to_uuid; -use super::FallibleApiResponse; +use crate::library::event::Event; +use crate::library::event::EventKind; +use crate::library::track::Track; +use based::auth::User; +use based::request::api::api_error; +use based::request::api::to_uuid; +use based::request::api::FallibleApiResponse; use rocket::post; use rocket::serde::json::Json; use serde::Deserialize; use serde_json::json; -use crate::library::event::Event; -use crate::library::event::EventKind; -use crate::library::track::Track; -use crate::library::user::User; - #[derive(Debug, Clone, Deserialize)] pub struct EventJson { pub kind: EventKind, diff --git a/src/route/mod.rs b/src/route/mod.rs index 6a62dee..06b8aec 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -16,42 +16,6 @@ pub mod search; pub mod track; pub mod user; -// todo : rework api - -/// A trait to generate a Model API representation in JSON format. -pub trait ToAPI: Sized { - /// Generate public API JSON - fn api(&self) -> impl std::future::Future; -} - -/// Converts a slice of items implementing the `ToAPI` trait into a `Vec` of JSON values. -pub async fn vec_to_api(items: &[impl ToAPI]) -> Vec { - let mut ret = Vec::with_capacity(items.len()); - - for e in items { - ret.push(e.api().await); - } - - ret -} - -pub fn to_uuid(id: &str) -> Result { - uuid::Uuid::from_str(id).map_err(|_| no_uuid_error()) -} - -type ApiError = BadRequest; -type FallibleApiResponse = Result; - -pub fn no_uuid_error() -> ApiError { - api_error("No valid UUID") -} - -pub fn api_error(msg: &str) -> ApiError { - BadRequest(json!({ - "error": msg - })) -} - #[get("/")] pub fn index_redir() -> Redirect { Redirect::to(uri!("/web")) diff --git a/src/route/playlist.rs b/src/route/playlist.rs index 65d550c..a1c8e26 100644 --- a/src/route/playlist.rs +++ b/src/route/playlist.rs @@ -1,19 +1,17 @@ use crate::get_pg; +use crate::library::playlist::Playlist; +use crate::library::playlist::Visibility; use crate::library::track::Track; -use crate::library::user::User; +use based::auth::User; +use based::request::api::api_error; +use based::request::api::to_uuid; +use based::request::api::vec_to_api; +use based::request::api::FallibleApiResponse; use rocket::get; use rocket::post; use rocket::serde::json::Json; use serde_json::json; -use crate::library::playlist::Playlist; -use crate::library::playlist::Visibility; -use crate::route::FallibleApiResponse; - -use super::api_error; -use super::to_uuid; -use super::vec_to_api; - #[get("/playlists")] pub async fn playlists_route(u: User) -> FallibleApiResponse { let mut playlists = vec![ diff --git a/src/route/search.rs b/src/route/search.rs index 666cb15..965bc6e 100644 --- a/src/route/search.rs +++ b/src/route/search.rs @@ -1,5 +1,4 @@ -use super::api_error; -use super::FallibleApiResponse; +use based::request::api::{api_error, FallibleApiResponse}; use rocket::get; use serde_json::json; diff --git a/src/route/track.rs b/src/route/track.rs index 9f03007..c53f75c 100644 --- a/src/route/track.rs +++ b/src/route/track.rs @@ -1,18 +1,13 @@ -use std::str::FromStr; - -use super::api_error; -use super::no_uuid_error; -use super::to_uuid; -use super::FallibleApiResponse; -use super::ToAPI; -use crate::library::user::User; +use crate::library::{track, Libary}; +use based::{ + auth::User, + check_admin, + request::api::{api_error, to_uuid, FallibleApiResponse, ToAPI}, +}; use fs::NamedFile; use rocket::{fs, get, State}; use serde_json::json; -use crate::check_admin; -use crate::library::Libary; - #[get("/track/")] pub async fn track_route(track_id: &str, lib: &State) -> FallibleApiResponse { Ok(lib @@ -38,9 +33,7 @@ pub async fn track_reload_meta_route( #[get("/track//audio")] pub async fn track_audio_route(track_id: &str, lib: &State) -> Option { - let track = lib - .get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?) - .await?; + let track = lib.get_track_by_id(&to_uuid(track_id).ok()?).await?; NamedFile::open(std::path::Path::new(&track.path)) .await .ok() @@ -48,24 +41,20 @@ pub async fn track_audio_route(track_id: &str, lib: &State) -> Option/audio/opus128")] pub async fn track_audio_opus128_route(track_id: &str, lib: &State) -> Option { - let track = lib - .get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?) - .await?; + let track = lib.get_track_by_id(&to_uuid(track_id).ok()?).await?; NamedFile::open(track.get_opus(128)?).await.ok() } #[get("/track//audio/aac128")] pub async fn track_audio_aac128_route(track_id: &str, lib: &State) -> Option { - let track = lib - .get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?) - .await?; + let track = lib.get_track_by_id(&to_uuid(track_id).ok()?).await?; NamedFile::open(track.get_aac(128)?).await.ok() } #[get("/track//lyrics")] pub async fn track_lyrics_route(track_id: &str, lib: &State) -> FallibleApiResponse { let track = lib - .get_track_by_id(&uuid::Uuid::from_str(track_id).map_err(|_| no_uuid_error())?) + .get_track_by_id(&to_uuid(track_id)?) .await .ok_or_else(|| api_error("No such track"))?; diff --git a/src/route/user.rs b/src/route/user.rs index 6725501..4abac17 100644 --- a/src/route/user.rs +++ b/src/route/user.rs @@ -1,46 +1,12 @@ -use crate::get_pg; -use crate::library::user::User; -use crate::route::vec_to_api; +use based::auth::{User, UserRole}; +use based::check_admin; +use based::request::api::{api_error, vec_to_api, FallibleApiResponse}; use rocket::get; -use rocket::http::Status; -use rocket::outcome::Outcome; use rocket::post; -use rocket::request::FromRequest; use rocket::serde::json::Json; -use rocket::Request; use serde::Deserialize; use serde_json::json; -use super::api_error; -use super::FallibleApiResponse; - -#[macro_export] -macro_rules! check_admin { - ($u:ident) => { - if !$u.is_admin() { - return Err(api_error("Forbidden")); - } - }; -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for User { - type Error = (); - - async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { - match request.headers().get_one("token") { - Some(key) => { - if let Some(user) = sqlx::query_as("SELECT * FROM users WHERE username = (SELECT \"user\" FROM user_session WHERE token = $1)").bind(key).fetch_optional(get_pg!()).await.unwrap() { - Outcome::Success(user) - } else { - Outcome::Error((Status::Unauthorized, ())) - } - } - None => Outcome::Error((Status::Unauthorized, ())), - } - } -} - #[derive(Deserialize)] pub struct LoginData { pub username: String, @@ -89,13 +55,9 @@ pub async fn users_route(u: User) -> FallibleApiResponse { pub async fn user_create_route(user: Json, u: User) -> FallibleApiResponse { check_admin!(u); - let new_user = User::create( - &user.username, - &user.password, - crate::library::user::UserRole::Regular, - ) - .await - .unwrap(); + let new_user = User::create(&user.username, &user.password, UserRole::Regular) + .await + .unwrap(); Ok(json!({"created": new_user.username})) }