devin15/cursor2api-rust
0
1use crate::{2 app::{3 constant::EMPTY_STRING,4 model::TokenInfo,5 lazy::{TOKEN_FILE, TOKEN_LIST_FILE},6 },7 common::utils::generate_checksum_with_default,8};9 10// 规范化文件内容并写入11fn normalize_and_write(content: &str, file_path: &str) -> String {12 let normalized = content.replace("\r\n", "\n");13 if normalized != content {14 if let Err(e) = std::fs::write(file_path, &normalized) {15 eprintln!("警告: 无法更新规范化的文件: {}", e);16 }17 }18 normalized19}20 21// 解析token22fn parse_token(token_part: &str) -> Option<String> {23 // 查找最后一个:或%3A的位置24 let colon_pos = token_part.rfind(':');25 let encoded_colon_pos = token_part.rfind("%3A");26 27 match (colon_pos, encoded_colon_pos) {28 (None, None) => Some(token_part.to_string()),29 (Some(pos1), None) => Some(token_part[(pos1 + 1)..].to_string()),30 (None, Some(pos2)) => Some(token_part[(pos2 + 3)..].to_string()),31 (Some(pos1), Some(pos2)) => {32 // 取较大的位置作为分隔点33 let pos = pos1.max(pos2);34 let start = if pos == pos2 { pos + 3 } else { pos + 1 };35 Some(token_part[start..].to_string())36 }37 }38}39 40// Token 加载函数41pub fn load_tokens() -> Vec<TokenInfo> {42 let token_file = TOKEN_FILE.as_str();43 let token_list_file = TOKEN_LIST_FILE.as_str();44 45 // 确保文件存在46 for file in [&token_file, &token_list_file] {47 if !std::path::Path::new(file).exists() {48 if let Err(e) = std::fs::write(file, EMPTY_STRING) {49 eprintln!("警告: 无法创建文件 '{}': {}", file, e);50 }51 }52 }53 54 // 读取和规范化 token 文件55 let token_entries = match std::fs::read_to_string(&token_file) {56 Ok(content) => {57 let normalized = content.replace("\r\n", "\n");58 normalized59 .lines()60 .filter_map(|line| {61 let line = line.trim();62 if line.is_empty() || line.starts_with('#') {63 return None;64 }65 let parsed = parse_token(line);66 if parsed.is_none() || !validate_token(&parsed.as_ref().unwrap()) {67 return None;68 }69 parsed70 })71 .collect::<Vec<_>>()72 }73 Err(e) => {74 eprintln!("警告: 无法读取token文件 '{}': {}", token_file, e);75 Vec::new()76 }77 };78 79 // 读取和规范化 token-list 文件80 let mut token_map: std::collections::HashMap<String, String> =81 match std::fs::read_to_string(&token_list_file) {82 Ok(content) => {83 let normalized = normalize_and_write(&content, &token_list_file);84 normalized85 .lines()86 .filter_map(|line| {87 let line = line.trim();88 if line.is_empty() || line.starts_with('#') {89 return None;90 }91 92 let parts: Vec<&str> = line.split(',').collect();93 match parts[..] {94 [token_part, checksum] => {95 let token = parse_token(token_part)?;96 Some((token, checksum.to_string()))97 }98 _ => {99 eprintln!("警告: 忽略无效的token-list行: {}", line);100 None101 }102 }103 })104 .collect()105 }106 Err(e) => {107 eprintln!("警告: 无法读取token-list文件: {}", e);108 std::collections::HashMap::new()109 }110 };111 112 // 更新或添加新token113 for token in token_entries {114 if !token_map.contains_key(&token) {115 // 为新token生成checksum116 let checksum = generate_checksum_with_default();117 token_map.insert(token, checksum);118 }119 }120 121 // 更新 token-list 文件122 let token_list_content = token_map123 .iter()124 .map(|(token, checksum)| {125 format!("{},{}", token, checksum)126 })127 .collect::<Vec<_>>()128 .join("\n");129 130 if let Err(e) = std::fs::write(&token_list_file, token_list_content) {131 eprintln!("警告: 无法更新token-list文件: {}", e);132 }133 134 // 转换为 TokenInfo vector135 token_map136 .into_iter()137 .map(|(token, checksum)| TokenInfo {138 token: token.clone(),139 checksum,140 profile: None,141 })142 .collect()143}144 145use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};146use chrono::{DateTime, Local, TimeZone};147 148// 验证jwt token是否有效149pub fn validate_token(token: &str) -> bool {150 // 检查 token 格式151 let parts: Vec<&str> = token.split('.').collect();152 if parts.len() != 3 {153 return false;154 }155 156 if parts[0] != "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" {157 return false;158 }159 160 // 解码 payload161 let payload = match URL_SAFE_NO_PAD.decode(parts[1]) {162 Ok(decoded) => decoded,163 Err(_) => return false,164 };165 166 // 转换为字符串167 let payload_str = match String::from_utf8(payload) {168 Ok(s) => s,169 Err(_) => return false,170 };171 172 // 解析 JSON173 let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) {174 Ok(v) => v,175 Err(_) => return false,176 };177 178 // 验证必要字段是否存在且有效179 let required_fields = ["sub", "time", "randomness", "exp", "iss", "scope", "aud"];180 for field in required_fields {181 if !payload_json.get(field).is_some() {182 return false;183 }184 }185 186 // 验证 time 字段187 if let Some(time) = payload_json["time"].as_str() {188 // 验证 time 是否为有效的数字字符串189 if let Ok(time_value) = time.parse::<i64>() {190 let current_time = chrono::Utc::now().timestamp();191 if time_value > current_time {192 return false;193 }194 } else {195 return false;196 }197 } else {198 return false;199 }200 201 // 验证 randomness 长度202 if let Some(randomness) = payload_json["randomness"].as_str() {203 if randomness.len() != 18 {204 return false;205 }206 } else {207 return false;208 }209 210 // 验证过期时间211 if let Some(exp) = payload_json["exp"].as_i64() {212 let current_time = chrono::Utc::now().timestamp();213 if current_time > exp {214 return false;215 }216 } else {217 return false;218 }219 220 // 验证发行者221 if payload_json["iss"].as_str() != Some("https://authentication.cursor.sh") {222 return false;223 }224 225 // 验证授权范围226 if payload_json["scope"].as_str() != Some("openid profile email offline_access") {227 return false;228 }229 230 // 验证受众231 if payload_json["aud"].as_str() != Some("https://cursor.com") {232 return false;233 }234 235 true236}237 238// 从 JWT token 中提取用户 ID239pub fn extract_user_id(token: &str) -> Option<String> {240 // JWT token 由3部分组成,用 . 分隔241 let parts: Vec<&str> = token.split('.').collect();242 if parts.len() != 3 {243 return None;244 }245 246 // 解码 payload (第二部分)247 let payload = match URL_SAFE_NO_PAD.decode(parts[1]) {248 Ok(decoded) => decoded,249 Err(_) => return None,250 };251 252 // 将 payload 转换为字符串253 let payload_str = match String::from_utf8(payload) {254 Ok(s) => s,255 Err(_) => return None,256 };257 258 // 解析 JSON259 let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) {260 Ok(v) => v,261 Err(_) => return None,262 };263 264 // 提取 sub 字段265 payload_json["sub"]266 .as_str()267 .map(|s| s.split('|').nth(1).unwrap_or(s).to_string())268}269 270// 从 JWT token 中提取 time 字段271pub fn extract_time(token: &str) -> Option<DateTime<Local>> {272 // JWT token 由3部分组成,用 . 分隔273 let parts: Vec<&str> = token.split('.').collect();274 if parts.len() != 3 {275 return None;276 }277 278 // 解码 payload (第二部分)279 let payload = match URL_SAFE_NO_PAD.decode(parts[1]) {280 Ok(decoded) => decoded,281 Err(_) => return None,282 };283 284 // 将 payload 转换为字符串285 let payload_str = match String::from_utf8(payload) {286 Ok(s) => s,287 Err(_) => return None,288 };289 290 // 解析 JSON291 let payload_json: serde_json::Value = match serde_json::from_str(&payload_str) {292 Ok(v) => v,293 Err(_) => return None,294 };295 296 // 提取时间戳并转换为本地时间297 payload_json["time"]298 .as_str()299 .and_then(|t| t.parse::<i64>().ok())300 .and_then(|timestamp| Local.timestamp_opt(timestamp, 0).single())301}302 