Team Ai
Apppublic

devin15/cursor2api-rust

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
tokens.rs302 linesDownload Raw Back to utils
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