diff --git a/src/api/GESE.rs b/src/api/GESE.rs new file mode 100644 index 0000000..2ccacc7 --- /dev/null +++ b/src/api/GESE.rs @@ -0,0 +1,172 @@ +use serde::{Deserialize, Serialize}; +use actix_web::{get, web, HttpResponse, Responder}; +use std::env; +use dotenv::dotenv; +use reqwest::header; + +// 补全建议的JSON结构 +#[derive(Debug, Serialize)] +pub struct CompletionResponse { + pub success: bool, + pub message: String, + pub suggestions: Vec, +} + +// 搜索结果的JSON结构 +#[derive(Debug, Serialize, Deserialize)] +pub struct SearchResult { + pub title: String, + pub link: String, + pub snippet: String, +} + +// API响应结构 +#[derive(Debug, Serialize)] +pub struct ApiResponse { + pub success: bool, + pub message: String, + pub data: Option>, + pub current_page: u32, + pub total_pages: u32, +} + +// 查询参数结构 +#[derive(Debug, Deserialize)] +pub struct SearchParams { + pub q: String, + pub page: Option, +} + +// 从Google CSE获取搜索结果 +pub async fn fetch_google_search_results( + query: &str, + start: u32, +) -> Result<(Vec, u32), Box> { + dotenv().ok(); + let api_key = env::var("GOOGLE_API_KEY").expect("GOOGLE_API_KEY must be set"); + let search_engine_id = env::var("SEARCH_ENGINE_ID").expect("SEARCH_ENGINE_ID must be set"); + + let client = reqwest::Client::new(); + let num_results = 10; // 每页10条结果 + + let response = client + .get("https://www.googleapis.com/customsearch/v1") + .query(&[ + ("key", &api_key), + ("cx", &search_engine_id), + ("q", &query.to_string()), + ("num", &num_results.to_string()), + ("start", &start.to_string()), + ]) + .send() + .await?; + + // 打印原始响应 + let raw_response = response.text().await?; + + let json: serde_json::Value = serde_json::from_str(&raw_response)?; + + // 获取总结果数并计算总页数 + let total_results = json["searchInformation"]["totalResults"] + .as_str() + .and_then(|s| s.parse::().ok()) // 使用u64处理大数字 + .unwrap_or(0); + + let total_pages = (total_results as f64 / num_results as f64).ceil() as u32; + + // 解析结果项 + let items = json["items"] + .as_array() + .map(|arr| { + arr.iter() + .map(|item| SearchResult { + title: item["title"].as_str().unwrap_or("").to_string(), + link: item["link"].as_str().unwrap_or("").to_string(), + snippet: item["snippet"].as_str().unwrap_or("").to_string(), + }) + .collect() + }) + .unwrap_or_else(Vec::new); + + Ok((items, total_pages)) +} + +// 获取Google搜索建议 +pub async fn fetch_google_suggestions(query: &str) -> Result, Box> { + let client = reqwest::Client::new(); + let url = format!("https://www.google.com/complete/search?q={}&client=chrome", query); + + let response = client + .get(&url) + .header(header::USER_AGENT, "Mozilla/5.0") + .send() + .await?; + + let suggestions: Vec = response + .json::>() + .await? + .get(1) + .and_then(|v| v.as_array()) + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + }) + .unwrap_or_else(Vec::new); + + Ok(suggestions) +} + +// 处理补全建议请求 +#[get("/completion")] +pub async fn completion(params: web::Query) -> impl Responder { + match fetch_google_suggestions(¶ms.q).await { + Ok(suggestions) => { + let response = CompletionResponse { + success: true, + message: "Suggestions fetched successfully".to_string(), + suggestions, + }; + HttpResponse::Ok().json(response) + } + Err(e) => { + let error_response = CompletionResponse { + success: false, + message: format!("Failed to fetch suggestions: {}", e), + suggestions: Vec::new(), + }; + HttpResponse::InternalServerError().json(error_response) + } + } +} + +// 处理搜索请求 +#[get("/search")] +pub async fn search(params: web::Query) -> impl Responder { + let current_page = params.page.unwrap_or(1); + let start = (current_page - 1) * 10 + 1; // Google CSE的start参数从1开始 + + match fetch_google_search_results(¶ms.q, start).await { + Ok((results, total_pages)) => { + + let response = ApiResponse { + success: true, + message: "Search successful".to_string(), + data: Some(results), + current_page, + total_pages, + }; + HttpResponse::Ok().json(response) + } + Err(e) => { + let error_response = ApiResponse { + success: false, + message: format!("Search failed: {}", e), + data: None, + current_page: 0, + total_pages: 0, + }; + HttpResponse::InternalServerError().json(error_response) + } + } +} \ No newline at end of file diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..3aa6471 --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1,2 @@ +// 导出GESE模块 +pub mod GESE; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 56cd4b6..c0bd789 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,184 +1,21 @@ -use actix_web::{get, web, App, HttpServer, Responder, HttpResponse}; -use serde::{Deserialize, Serialize}; -use std::env; +use actix_web::{web, App, HttpServer}; use dotenv::dotenv; -use reqwest::header; -// 补全建议的JSON结构 -#[derive(Debug, Serialize)] -struct CompletionResponse { - success: bool, - message: String, - suggestions: Vec, -} - -// 搜索结果的JSON结构 -#[derive(Debug, Serialize, Deserialize)] -struct SearchResult { - title: String, - link: String, - snippet: String, -} - -// API响应结构 -#[derive(Debug, Serialize)] -struct ApiResponse { - success: bool, - message: String, - data: Option>, - current_page: u32, - total_pages: u32, -} - -// 查询参数结构 -#[derive(Debug, Deserialize)] -struct SearchParams { - q: String, - page: Option, -} - -// 从Google CSE获取搜索结果 -async fn fetch_google_search_results( - query: &str, - start: u32, -) -> Result<(Vec, u32), Box> { - dotenv().ok(); - let api_key = env::var("GOOGLE_API_KEY").expect("GOOGLE_API_KEY must be set"); - let search_engine_id = env::var("SEARCH_ENGINE_ID").expect("SEARCH_ENGINE_ID must be set"); - - let client = reqwest::Client::new(); - let num_results = 10; // 每页10条结果 - - let response = client - .get("https://www.googleapis.com/customsearch/v1") - .query(&[ - ("key", &api_key), - ("cx", &search_engine_id), - ("q", &query.to_string()), - ("num", &num_results.to_string()), - ("start", &start.to_string()), - ]) - .send() - .await?; - - // 打印原始响应 - let raw_response = response.text().await?; - - let json: serde_json::Value = serde_json::from_str(&raw_response)?; - - // 获取总结果数并计算总页数 - let total_results = json["searchInformation"]["totalResults"] - .as_str() - .and_then(|s| s.parse::().ok()) // 使用u64处理大数字 - .unwrap_or(0); - - let total_pages = (total_results as f64 / num_results as f64).ceil() as u32; - - // 解析结果项 - let items = json["items"] - .as_array() - .map(|arr| { - arr.iter() - .map(|item| SearchResult { - title: item["title"].as_str().unwrap_or("").to_string(), - link: item["link"].as_str().unwrap_or("").to_string(), - snippet: item["snippet"].as_str().unwrap_or("").to_string(), - }) - .collect() - }) - .unwrap_or_else(Vec::new); - - Ok((items, total_pages)) -} - -// 处理搜索请求 -// 获取Google搜索建议 -async fn fetch_google_suggestions(query: &str) -> Result, Box> { - let client = reqwest::Client::new(); - let url = format!("https://www.google.com/complete/search?q={}&client=chrome", query); - - let response = client - .get(&url) - .header(header::USER_AGENT, "Mozilla/5.0") - .send() - .await?; - - let suggestions: Vec = response - .json::>() - .await? - .get(1) - .and_then(|v| v.as_array()) - .map(|arr| { - arr.iter() - .filter_map(|v| v.as_str().map(|s| s.to_string())) - .collect() - }) - .unwrap_or_else(Vec::new); - - Ok(suggestions) -} - -// 处理补全建议请求 -#[get("/completion")] -async fn completion(params: web::Query) -> impl Responder { - match fetch_google_suggestions(¶ms.q).await { - Ok(suggestions) => { - let response = CompletionResponse { - success: true, - message: "Suggestions fetched successfully".to_string(), - suggestions, - }; - HttpResponse::Ok().json(response) - } - Err(e) => { - let error_response = CompletionResponse { - success: false, - message: format!("Failed to fetch suggestions: {}", e), - suggestions: Vec::new(), - }; - HttpResponse::InternalServerError().json(error_response) - } - } -} - -#[get("/search")] -async fn search(params: web::Query) -> impl Responder { - let current_page = params.page.unwrap_or(1); - let start = (current_page - 1) * 10 + 1; // Google CSE的start参数从1开始 - - match fetch_google_search_results(¶ms.q, start).await { - Ok((results, total_pages)) => { - - let response = ApiResponse { - success: true, - message: "Search successful".to_string(), - data: Some(results), - current_page, - total_pages, - }; - HttpResponse::Ok().json(response) - } - Err(e) => { - let error_response = ApiResponse { - success: false, - message: format!("Search failed: {}", e), - data: None, - current_page: 0, - total_pages: 0, - }; - HttpResponse::InternalServerError().json(error_response) - } - } -} +// 导入api模块 +mod api; +use api::GESE; #[actix_web::main] async fn main() -> std::io::Result<()> { + // 确保环境变量加载 + dotenv().ok(); + println!("Server started at http://0.0.0.0:8080"); HttpServer::new(|| { App::new() - .service(search) - .service(completion) + .service(GESE::search) // 注册Search API + .service(GESE::completion) // 注册Completion API }) .bind("0.0.0.0:8080")? .run()