1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
| use actix_web::{post, web, App, HttpServer, Responder, HttpResponse, HttpRequest}; use serde::{Deserialize, Serialize}; use std::path::Path; use std::fs; use reqwest::Client; use serde_json::json;
#[derive(Serialize, Deserialize)] struct SummaryRequest { content: String, url: String, }
#[derive(Serialize, Deserialize)] struct SummaryResponse { url: String, summary: String, }
struct AppState { auth_token: String, }
const SUMMARY_DIR: &str = "summaries";
fn is_valid_referer(referer: Option<&str>) -> bool { if let Some(ref_url) = referer { let valid_domains = vec![ ".ordchaos.com", ".ordchaos.top", ".ordchaos.eu.org", ]; return valid_domains.iter().any(|domain| ref_url.ends_with(domain)); } false }
async fn generate_summary(content: &str) -> Result<String, Box<dyn std::error::Error>> { let api_key = "*******************************"; let api_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"; let client = Client::new();
let request_body = json!({ "model": "qwen-long", "messages": [ {"role": "system", "content": "You are a helpful summary generator."}, {"role": "user", "content": format!("请为以下内容用中文生成长度为150汉字左右的摘要,摘要只有一个自然段,且只给出摘要即可,不要说其他任何话: {}", content)} ], "temperature": 0.8, "top_p": 0.8 });
let response = client .post(format!("{}/chat/completions", api_url)) .header("Authorization", format!("Bearer {}", api_key)) .json(&request_body) .send() .await?;
let response_json: serde_json::Value = response.json().await?; let summary = response_json["choices"][0]["message"]["content"] .as_str() .unwrap_or("摘要生成失败:返回格式不正确") .to_string();
Ok(summary.trim().to_string()) }
fn save_summary(abbrlink: &str, summary_data: &SummaryResponse) -> std::io::Result<()> { let summary_path = format!("{}/{}.json", SUMMARY_DIR, abbrlink); let json_data = serde_json::to_string(summary_data)?; fs::write(&summary_path, &json_data)?; Ok(()) }
fn load_summary(abbrlink: &str) -> Option<SummaryResponse> { let summary_path = format!("{}/{}.json", SUMMARY_DIR, abbrlink); if Path::new(&summary_path).exists() { if let Ok(json_data) = fs::read_to_string(&summary_path) { if let Ok(summary) = serde_json::from_str(&json_data) { return Some(summary); } } } None }
#[post("/generate-summary")] async fn generate_summary_handler( data: web::Data<AppState>, req: HttpRequest, summary_request: web::Json<SummaryRequest> ) -> impl Responder { if let Some(auth_header) = req.headers().get("Authorization") { let token = auth_header.to_str().unwrap_or("").trim_start_matches("Bearer "); if token != data.auth_token { return HttpResponse::Unauthorized().body("鉴权失败:Token不正确"); } } else { return HttpResponse::Unauthorized().body("鉴权失败:缺失Token"); }
let referer = req.headers().get("Referer").and_then(|v| v.to_str().ok()); let origin = req.headers().get("Origin").and_then(|v| v.to_str().ok()); if !is_valid_referer(referer) && !is_valid_referer(origin) { return HttpResponse::Forbidden().body("拒绝生成摘要:来源不正确"); }
let abbrlink = summary_request.url .split('/') .filter(|&segment| !segment.is_empty()) .last() .unwrap_or("");
if abbrlink.is_empty() { return HttpResponse::BadRequest().body("Invalid URL format"); }
if let Some(existing_summary) = load_summary(abbrlink) { return HttpResponse::Ok().json(existing_summary); }
let summary = match generate_summary(&summary_request.content).await { Ok(summary) => summary, Err(_) => return HttpResponse::InternalServerError().body("摘要生成失败"), };
let summary_data = SummaryResponse { url: summary_request.url.clone(), summary: summary.clone(), };
if let Err(err) = save_summary(abbrlink, &summary_data) { return HttpResponse::InternalServerError().body(format!("存储摘要失败: {}", err)); }
HttpResponse::Ok().json(summary_data) }
#[actix_web::main] async fn main() -> std::io::Result<()> { fs::create_dir_all(SUMMARY_DIR)?; let auth_token = std::env::var("AUTH_TOKEN").expect("AUTH_TOKEN 未被设置");
println!("服务端已经在 http://localhost:11451 开启");
HttpServer::new(move || { App::new() .app_data(web::Data::new(AppState { auth_token: auth_token.clone(), })) .service(generate_summary_handler) }) .bind("127.0.0.1:11451")? .run() .await }
|