use crate::{ app::{ constant::EMPTY_STRING, model::TokenInfo, lazy::{TOKEN_FILE, TOKEN_LIST_FILE}, }, common::utils::generate_checksum_with_default, }; // 规范化文件内容并写入 fn normalize_and_write(content: &str, file_path: &str) -> String { let normalized = content.replace("\r\n", "\n"); if normalized != content { if let Err(e) = std::fs::write(file_path, &normalized) { eprintln!("警告: 无法更新规范化的文件: {}", e); } } normalized } // 解析token fn parse_token(token_part: &str) -> Option { // 查找最后一个:或%3A的位置 let colon_pos = token_part.rfind(':'); let encoded_colon_pos = token_part.rfind("%3A"); match (colon_pos, encoded_colon_pos) { (None, None) => Some(token_part.to_string()), (Some(pos1), None) => Some(token_part[(pos1 + 1)..].to_string()), (None, Some(pos2)) => Some(token_part[(pos2 + 3)..].to_string()), (Some(pos1), Some(pos2)) => { // 取较大的位置作为分隔点 let pos = pos1.max(pos2); let start = if pos == pos2 { pos + 3 } else { pos + 1 }; Some(token_part[start..].to_string()) } } } // Token 加载函数 pub fn load_tokens() -> Vec { let token_file = TOKEN_FILE.as_str(); let token_list_file = TOKEN_LIST_FILE.as_str(); // 确保文件存在 for file in [&token_file, &token_list_file] { if !std::path::Path::new(file).exists() { if let Err(e) = std::fs::write(file, EMPTY_STRING) { eprintln!("警告: 无法创建文件 '{}': {}", file, e); } } } // 读取和规范化 token 文件 let token_entries = match std::fs::read_to_string(&token_file) { Ok(content) => { let normalized = content.replace("\r\n", "\n"); normalized .lines() .filter_map(|line| { let line = line.trim(); if line.is_empty() || line.starts_with('#') { return None; } let parsed = parse_token(line); if parsed.is_none() || !validate_token(&parsed.as_ref().unwrap()) { return None; } parsed }) .collect::>() } Err(e) => { eprintln!("警告: 无法读取token文件 '{}': {}", token_file, e); Vec::new() } }; // 读取和规范化 token-list 文件 let mut token_map: std::collections::HashMap = match std::fs::read_to_string(&token_list_file) { Ok(content) => { let normalized = normalize_and_write(&content, &token_list_file); normalized .lines() .filter_map(|line| { let line = line.trim(); if line.is_empty() || line.starts_with('#') { return None; } let parts: Vec<&str> = line.split(',').collect(); match parts[..] { [token_part, checksum] => { let token = parse_token(token_part)?; Some((token, checksum.to_string())) } _ => { eprintln!("警告: 忽略无效的token-list行: {}", line); None } } }) .collect() } Err(e) => { eprintln!("警告: 无法读取token-list文件: {}", e); std::collections::HashMap::new() } }; // 更新或添加新token for token in token_entries { if !token_map.contains_key(&token) { // 为新token生成checksum let checksum = generate_checksum_with_default(); token_map.insert(token, checksum); } } // 更新 token-list 文件 let token_list_content = token_map .iter() .map(|(token, checksum)| { format!("{},{}", token, checksum) }) .collect::>() .join("\n"); if let Err(e) = std::fs::write(&token_list_file, token_list_content) { eprintln!("警告: 无法更新token-list文件: {}", e); } // 转换为 TokenInfo vector token_map .into_iter() .map(|(token, checksum)| TokenInfo { token: token.clone(), checksum, profile: None, }) .collect() } use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use chrono::{DateTime, Local, TimeZone}; // 验证jwt token是否有效 pub fn validate_token(token: &str) -> bool { // 检查 token 格式 let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { return false; } if parts[0] != "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" { return false; } // 解码 payload let payload = match URL_SAFE_NO_PAD.decode(parts[1]) { Ok(decoded) => decoded, Err(_) => return false, }; // 转换为字符串 let payload_str = match String::from_utf8(payload) { Ok(s) => s, Err(_) => return false, }; // 解析 JSON let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) { Ok(v) => v, Err(_) => return false, }; // 验证必要字段是否存在且有效 let required_fields = ["sub", "time", "randomness", "exp", "iss", "scope", "aud"]; for field in required_fields { if !payload_json.get(field).is_some() { return false; } } // 验证 time 字段 if let Some(time) = payload_json["time"].as_str() { // 验证 time 是否为有效的数字字符串 if let Ok(time_value) = time.parse::() { let current_time = chrono::Utc::now().timestamp(); if time_value > current_time { return false; } } else { return false; } } else { return false; } // 验证 randomness 长度 if let Some(randomness) = payload_json["randomness"].as_str() { if randomness.len() != 18 { return false; } } else { return false; } // 验证过期时间 if let Some(exp) = payload_json["exp"].as_i64() { let current_time = chrono::Utc::now().timestamp(); if current_time > exp { return false; } } else { return false; } // 验证发行者 if payload_json["iss"].as_str() != Some("https://authentication.cursor.sh") { return false; } // 验证授权范围 if payload_json["scope"].as_str() != Some("openid profile email offline_access") { return false; } // 验证受众 if payload_json["aud"].as_str() != Some("https://cursor.com") { return false; } true } // 从 JWT token 中提取用户 ID pub fn extract_user_id(token: &str) -> Option { // JWT token 由3部分组成,用 . 分隔 let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { return None; } // 解码 payload (第二部分) let payload = match URL_SAFE_NO_PAD.decode(parts[1]) { Ok(decoded) => decoded, Err(_) => return None, }; // 将 payload 转换为字符串 let payload_str = match String::from_utf8(payload) { Ok(s) => s, Err(_) => return None, }; // 解析 JSON let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) { Ok(v) => v, Err(_) => return None, }; // 提取 sub 字段 payload_json["sub"] .as_str() .map(|s| s.split('|').nth(1).unwrap_or(s).to_string()) } // 从 JWT token 中提取 time 字段 pub fn extract_time(token: &str) -> Option> { // JWT token 由3部分组成,用 . 分隔 let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { return None; } // 解码 payload (第二部分) let payload = match URL_SAFE_NO_PAD.decode(parts[1]) { Ok(decoded) => decoded, Err(_) => return None, }; // 将 payload 转换为字符串 let payload_str = match String::from_utf8(payload) { Ok(s) => s, Err(_) => return None, }; // 解析 JSON let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) { Ok(v) => v, Err(_) => return None, }; // 提取时间戳并转换为本地时间 payload_json["time"] .as_str() .and_then(|t| t.parse::().ok()) .and_then(|timestamp| Local.timestamp_opt(timestamp, 0).single()) }