diff --git a/.gitignore b/.gitignore index cc1073d..5562fa5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /Assets-main /avatars /logs +/assets output.log docker-compose.yml Config.toml diff --git a/Cargo.lock b/Cargo.lock index 8badd8d..10fc1a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -864,6 +864,7 @@ checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1486,6 +1487,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.24" @@ -1503,7 +1513,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sculptor" -version = "0.3.1" +version = "0.4.0-dev" dependencies = [ "anyhow", "axum", @@ -1513,6 +1523,7 @@ dependencies = [ "dashmap", "dotenvy", "hex", + "indexmap 2.5.0", "rand", "reqwest", "ring", @@ -1528,6 +1539,7 @@ dependencies = [ "tracing-panic", "tracing-subscriber", "uuid", + "walkdir", ] [[package]] @@ -2243,6 +2255,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index dd73afc..7efb173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sculptor" authors = ["Shiroyashik "] -version = "0.3.1" +version = "0.4.0-dev" edition = "2021" publish = false @@ -28,6 +28,7 @@ base64 = "0.22.1" reqwest = { version = "0.12.6", features = ["json"] } dotenvy = "0.15.7" semver = "1.0.23" +walkdir = "2.5.0" # Crypto ring = "0.17.8" @@ -37,6 +38,7 @@ rand = "0.8.5" axum = { version = "0.7.5", features = ["ws", "macros", "http2"] } tower-http = { version = "0.5.2", features = ["trace"] } tokio = { version = "1.37.0", features = ["full"] } +indexmap = { version = "2.5.0", features = ["serde"] } [dev-dependencies] cross = "0.2.5" diff --git a/src/api/figura/assets.rs b/src/api/figura/assets.rs new file mode 100644 index 0000000..eadc7f5 --- /dev/null +++ b/src/api/figura/assets.rs @@ -0,0 +1,69 @@ +use std::path::PathBuf; + +use axum::{extract::Path, routing::get, Json, Router}; +use indexmap::IndexMap; +use ring::digest::{digest, SHA256}; +use serde_json::{json, Value}; +use tokio::{fs, io::AsyncReadExt as _}; +use walkdir::WalkDir; + +use crate::{api::errors::internal_and_log, ApiError, ApiResult, AppState}; + +pub fn router() -> Router { + Router::new() + .route("/", get(versions)) + .route("/v1", get(v1)) + .route("/v2", get(v2)) + .route("/*path", get(download)) +} + +async fn versions() -> Json { + Json(json!(["v1", "v2"])) +} + +async fn v1() -> ApiResult>> { + let map = index_assets("v1").await.map_err(|err| internal_and_log(err))?; + Ok(Json(map)) +} + +async fn v2() -> ApiResult>> { + let map = index_assets("v2").await.map_err(|err| internal_and_log(err))?; + Ok(Json(map)) +} + +async fn download(Path(path): Path) -> ApiResult> { + let mut file = if let Ok(file) = fs::File::open(format!("assets/{path}")).await { + file + } else { + return Err(ApiError::NotFound) + }; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).await.map_err(|err| internal_and_log(err))?; + Ok(buffer) +} + +// non web + +async fn index_assets(version: &str) -> anyhow::Result> { + let mut map = IndexMap::new(); + let version_path = PathBuf::from("assets/").join(version); + + for entry in WalkDir::new(version_path.clone()).into_iter().filter_map(|e| e.ok()) { + let data = match fs::read(entry.path()).await { + Ok(d) => d, + Err(_) => continue + }; + + let path: String; + + if cfg!(windows) { + path = entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string().replace("\\", "/"); + } else { + path = entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string(); + } + + map.insert(path, Value::from(hex::encode(digest(&SHA256, &data).as_ref()))); + } + + Ok(map) +} \ No newline at end of file diff --git a/src/api/figura/mod.rs b/src/api/figura/mod.rs index d098984..d7986a2 100644 --- a/src/api/figura/mod.rs +++ b/src/api/figura/mod.rs @@ -3,5 +3,6 @@ mod websocket; pub mod auth; pub mod profile; pub mod info; +pub mod assets; pub use websocket::handler as ws; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 34e7c0d..5bf69cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ pub use api::errors::{ApiResult, ApiError}; // API mod api; use api::{ - figura::{ws, info as api_info, profile as api_profile, auth as api_auth}, + figura::{ws, info as api_info, profile as api_profile, auth as api_auth, assets as api_assets}, // v1::{}, }; @@ -84,12 +84,12 @@ async fn main() -> Result<()> { .with(terminal_layer) .init(); - std::panic::set_hook(Box::new(panic_hook)); - // let prev_hook = std::panic::take_hook(); - // std::panic::set_hook(Box::new(move |panic_info| { - // panic_hook(panic_info); - // prev_hook(panic_info); - // })); + // std::panic::set_hook(Box::new(panic_hook)); + let prev_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + panic_hook(panic_info); + prev_hook(panic_info); + })); info!("The Sculptor v{}{}", SCULPTOR_VERSION, check_updates(REPOSITORY, &SCULPTOR_VERSION).await?); @@ -141,6 +141,7 @@ async fn main() -> Result<()> { let api = Router::new() .nest("//auth", api_auth::router()) // => /api//auth ¯\_(ツ)_/¯ + .nest("//assets", api_assets::router()) .nest("/v1", api::v1::router()) .route("/limits", get(api_info::limits)) .route("/version", get(api_info::version))