commit c07410b1a7faa7f8a85294888bc5a823c9f63a30 Author: BloxerHD Date: Wed Jul 9 23:09:30 2025 +0100 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37dace6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +Cargo.lock +.env +/miis \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3528328 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cache-mii" +version = "0.1.0" +edition = "2024" + +[dependencies] +rocket = "0.5.1" +tokio = { version = "1", features = ["full"] } +dotenvy = "0.15.7" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = { version = "0.12.12", features = ["json"] } +bytes = "1" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8418c68 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Cache Mii +Mii Render Caching Server that saves a .png and .tga render of a user's mii for use across SPFN's services + +### Credits: +- [Mii Renderer](https://mii-unsecure.ariankordi.net/): Arian Kordi diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..74cf889 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,112 @@ +use dotenvy::dotenv; +use rocket::http::Status; +use rocket::fs::NamedFile; +use rocket::State; +use reqwest::Client; +use bytes::Bytes; + +static MII_RENDERER_URL: &str = "https://mii-unsecure.ariankordi.net/"; + +static ALLOWED_FORMATS: [&str; 2] = [ + "png", + "tga", +]; + +async fn request_mii_render(client: &State, mii_data: &str, ext: &str) -> Option { + let req_str = format!("{}/miis/image.{}?data={}", MII_RENDERER_URL, ext, mii_data); + + let response = client.get(req_str) + .send() + .await + .ok()?; + + Some(response.bytes().await.expect("Failed to parse Mii Render to Bytes")) +} + +#[rocket::get("//")] +pub async fn mii_render(pid_str: &str, path: &str, client: &State) -> Option { + let ext = path.splitn(2, ".").nth(1)?; + let pid: i32 = pid_str.parse().expect("PID is not an i32"); + + if !ALLOWED_FORMATS.contains(&ext) {return None} + + let mii_data = { + let route = format!("https://account.spfn.net/api/v2/users/{}/mii", pid); + let response = client.get(route) + .send() + .await + .ok() + .expect("Error Retrieving Mii Data"); + + let data: String = response.json().await.expect("Failed to parse Mii Data Response"); + + data + }; + + let cache_path = std::path::Path::new("miis/").join(pid.to_string()); + let img_path = cache_path.clone().join(path); + let data_path = cache_path.clone().join("data"); + + // Update Cache + if cache_path.exists() && cache_path.is_dir() { // Mii data was previously cached - Verify it is correct + if data_path.exists() && data_path.is_file() { // Data exists - Check if it's the same + let cache_data = std::fs::read_to_string(&data_path); + + match cache_data { + Ok(hash) => { + if hash == mii_data { // Cached images are correct + if !img_path.exists() || !img_path.is_file() { // File type was not found - Request data + let img_data = request_mii_render(client, &mii_data, ext).await?; + let _ = std::fs::write(&img_path, &img_data); + } + } else { // Cached images are outdated - Update data + let img_data = request_mii_render(client, &mii_data, ext).await?; + let _ = std::fs::write(&img_path, &img_data); + }; + } + + Err(_) => { // Data cannot be read - Request data + println!("Data is not valid"); + let _ = std::fs::write(data_path, &mii_data); + + let img_data = request_mii_render(client, &mii_data, ext).await?; + let _ = std::fs::write(&img_path, &img_data); + } + } + } else { // Data isn't saved - Request data + println!("Data is not recorded"); + let _ = std::fs::write(data_path, &mii_data); + + let img_file = request_mii_render(client, &mii_data, ext).await?; + let _ = std::fs::write(&img_path, &img_file); + } + } else { // Mii data was never cached - Request data + println!("Mii Data was Never Cached"); + let _ = std::fs::create_dir(format!("miis/{}", pid.to_string())); + + let _ = std::fs::write(data_path, &mii_data); + + let img_data = request_mii_render(client, &mii_data, ext).await?; + let _ = std::fs::write(&img_path, &img_data); + }; + + let img = NamedFile::open(img_path).await.map_err(|_| Status::InternalServerError).expect("Error opening Image"); + + Some(img) +} + +#[rocket::launch] +async fn launch() -> _ { + dotenv().ok(); + + let client = Client::new(); + + let mii_path = std::path::Path::new("miis"); + if !mii_path.exists() || !mii_path.is_dir() { + let _ = std::fs::create_dir("miis"); + } + + rocket::build() + .manage(client) + .mount("/", rocket::routes![mii_render]) +} \ No newline at end of file