From ac98af14287ce7ebf2062c707fed89d656ba0786 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Mon, 9 Feb 2026 00:01:07 +0100 Subject: [PATCH] feat(scraper): added search option --- scraper/src/state.rs | 25 +++++++++++++++++++++++-- scraper/src/types.rs | 10 ++++++++++ server/src/state.rs | 8 ++++++++ server/src/state/proxy.rs | 15 +++++++++++++++ server/src/user.rs | 22 +++++++++++++++++++++- 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/scraper/src/state.rs b/scraper/src/state.rs index 96c8e2c..41a9e70 100644 --- a/scraper/src/state.rs +++ b/scraper/src/state.rs @@ -135,7 +135,7 @@ impl State { pub async fn get_user( &self, name: &str, - ) -> Result, GetUserError> { + ) -> Result, FetchJsonError> { let req = self.get(format!("/getuser/{name}"), None).await?; let text = req.text().await?; let json = serde_json::from_str::(&text)?; @@ -204,10 +204,31 @@ impl State { self.get("/friends/", None).await?.text().await?, )) } + + pub async fn search( + &self, + keyword: impl AsRef, + relation: i32, + ) -> Result>, FetchJsonError> { + //search// + let text = self + .get(format!("/search/{}/{relation}", keyword.as_ref()), None) + .await? + .text() + .await?; + log::debug!("Search result => {text:?}"); + if text.is_empty() { + return Ok(None); + }; + + let ret = serde_json::from_str(&text)?; + + Ok(ret) + } } #[derive(Debug, thiserror::Error)] -pub enum GetUserError { +pub enum FetchJsonError { #[error("RequestError: {0}")] RequestError(#[from] reqwest::Error), #[error("JsonError: {0}")] diff --git a/scraper/src/types.rs b/scraper/src/types.rs index 5afda35..75fcb68 100644 --- a/scraper/src/types.rs +++ b/scraper/src/types.rs @@ -115,3 +115,13 @@ pub struct Profile { pub discord: Option, pub github: Option, } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] +#[serde(tag = "type")] +pub enum Search { + #[serde(rename = "project")] + Project { v: SmolStr, s: SmolStr }, + + #[serde(rename = "user")] + User { v: SmolStr, s: SmolStr }, +} diff --git a/server/src/state.rs b/server/src/state.rs index 02a24c3..e49161f 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -59,6 +59,14 @@ impl GlobalState { ) -> color_eyre::Result> { proxy::getuser(self, login).await } + + pub async fn search( + &self, + keyword: String, + friends_only: i32, + ) -> color_eyre::Result>> { + proxy::search(self, keyword, friends_only).await + } } impl GlobalState {} diff --git a/server/src/state/proxy.rs b/server/src/state/proxy.rs index a4f70a4..37f564a 100644 --- a/server/src/state/proxy.rs +++ b/server/src/state/proxy.rs @@ -23,3 +23,18 @@ pub async fn getuser( ) -> color_eyre::Result> { Ok(self_.scraper.get_user(login.as_str()).await?) } + +#[cached( + time = 3, + size = 100, + result = true, + key = "(String, i32)", + convert = "{ (keyword.clone(), friends_only) }" +)] +pub async fn search( + self_: &GlobalState, + keyword: String, + friends_only: i32, +) -> color_eyre::Result>> { + Ok(self_.scraper.search(keyword, friends_only).await?) +} diff --git a/server/src/user.rs b/server/src/user.rs index 1eb3406..068d2d3 100644 --- a/server/src/user.rs +++ b/server/src/user.rs @@ -6,7 +6,9 @@ use axum::{ }; pub fn router() -> Router { - Router::new().route("/getuser/{login}", get(getuser_handler)) + Router::new() + .route("/getuser/{login}", get(getuser_handler)) + .route("/search/{keyword}/{friends_only}", get(search_handler)) } #[cfg_attr(debug_assertions, axum::debug_handler)] @@ -22,3 +24,21 @@ async fn getuser_handler( .map_err(|()| StatusCode::INTERNAL_SERVER_ERROR) .and_then(|o| o.ok_or(StatusCode::NOT_FOUND).map(Json)) } + +// /search// +async fn search_handler( + State(state): State, + Path((keyword, friends_only)): Path<(String, i32)>, +) -> Result>, StatusCode> { + log::info!( + "Searching `{keyword}` (friends_only: {})", + friends_only == 1, + ); + state + .search(keyword, friends_only) + .await + .map_err(crate::report_error) + .map_err(|()| StatusCode::INTERNAL_SERVER_ERROR)? + .ok_or(StatusCode::BAD_REQUEST) + .map(Json) +}