devin15/cursor2api-rust
0
1use crate::{2 app::constant::{3 ERR_INVALID_PATH, ERR_RESET_CONFIG, ERR_UPDATE_CONFIG, ROUTE_ABOUT_PATH, ROUTE_CONFIG_PATH,4 ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH, ROUTE_SHARED_JS_PATH,5 ROUTE_SHARED_STYLES_PATH, ROUTE_TOKENINFO_PATH, ROUTE_API_PATH,6 },7 common::models::userinfo::TokenProfile,8};9use crate::chat::model::Message;10use std::sync::{LazyLock, RwLock};11use serde::{Deserialize, Serialize};12 13// 页面内容类型枚举14#[derive(Clone, Serialize, Deserialize)]15#[serde(tag = "type", content = "content")]16pub enum PageContent {17 #[serde(rename = "default")]18 Default, // 默认行为19 #[serde(rename = "text")]20 Text(String), // 纯文本21 #[serde(rename = "html")]22 Html(String), // HTML 内容23}24 25impl Default for PageContent {26 fn default() -> Self {27 Self::Default28 }29}30 31mod usage_check;32pub use usage_check::UsageCheck;33 34// 静态配置35#[derive(Clone)]36pub struct AppConfig {37 stream_check: bool,38 stop_stream: bool,39 vision_ability: VisionAbility,40 slow_pool: bool,41 allow_claude: bool,42 pages: Pages,43 usage_check: UsageCheck,44}45 46#[derive(Serialize, Deserialize, Clone)]47pub enum VisionAbility {48 #[serde(rename = "none", alias = "disabled")]49 None,50 #[serde(rename = "base64", alias = "base64-only")]51 Base64,52 #[serde(rename = "all", alias = "base64-http")]53 All,54}55 56impl VisionAbility {57 pub fn from_str(s: &str) -> Self {58 match s.to_lowercase().as_str() {59 "none" | "disabled" => Self::None,60 "base64" | "base64-only" => Self::Base64,61 "all" | "base64-http" => Self::All,62 _ => Self::default(),63 }64 }65}66 67impl Default for VisionAbility {68 fn default() -> Self {69 Self::Base6470 }71}72 73#[derive(Clone, Default)]74pub struct Pages {75 pub root_content: PageContent,76 pub logs_content: PageContent,77 pub config_content: PageContent,78 pub tokeninfo_content: PageContent,79 pub shared_styles_content: PageContent,80 pub shared_js_content: PageContent,81 pub about_content: PageContent,82 pub readme_content: PageContent,83 pub api_content: PageContent,84}85 86// 运行时状态87pub struct AppState {88 pub total_requests: u64,89 pub active_requests: u64,90 pub error_requests: u64,91 pub request_logs: Vec<RequestLog>,92 pub token_infos: Vec<TokenInfo>,93}94 95// 全局配置实例96pub static APP_CONFIG: LazyLock<RwLock<AppConfig>> = LazyLock::new(|| {97 RwLock::new(AppConfig::default())98});99 100impl Default for AppConfig {101 fn default() -> Self {102 Self {103 stream_check: true,104 stop_stream: true,105 vision_ability: VisionAbility::Base64,106 slow_pool: false,107 allow_claude: false,108 pages: Pages::default(),109 usage_check: UsageCheck::Default,110 }111 }112}113 114macro_rules! config_methods {115 ($($field:ident: $type:ty, $default:expr;)*) => {116 $(117 paste::paste! {118 pub fn [<get_ $field>]() -> $type {119 APP_CONFIG120 .read()121 .map(|config| config.$field.clone())122 .unwrap_or($default)123 }124 125 pub fn [<update_ $field>](value: $type) -> Result<(), &'static str> {126 if let Ok(mut config) = APP_CONFIG.write() {127 config.$field = value;128 Ok(())129 } else {130 Err(ERR_UPDATE_CONFIG)131 }132 }133 134 pub fn [<reset_ $field>]() -> Result<(), &'static str> {135 if let Ok(mut config) = APP_CONFIG.write() {136 config.$field = $default;137 Ok(())138 } else {139 Err(ERR_RESET_CONFIG)140 }141 }142 }143 )*144 };145}146 147impl AppConfig {148 pub fn init(149 stream_check: bool,150 stop_stream: bool,151 vision_ability: VisionAbility,152 slow_pool: bool,153 allow_claude: bool,154 ) {155 if let Ok(mut config) = APP_CONFIG.write() {156 config.stream_check = stream_check;157 config.stop_stream = stop_stream;158 config.vision_ability = vision_ability;159 config.slow_pool = slow_pool;160 config.allow_claude = allow_claude;161 }162 }163 164 config_methods! {165 stream_check: bool, true;166 stop_stream: bool, true;167 slow_pool: bool, false;168 allow_claude: bool, false;169 }170 171 pub fn get_vision_ability() -> VisionAbility {172 APP_CONFIG173 .read()174 .map(|config| config.vision_ability.clone())175 .unwrap_or_default()176 }177 178 pub fn get_page_content(path: &str) -> Option<PageContent> {179 APP_CONFIG.read().ok().map(|config| match path {180 ROUTE_ROOT_PATH => config.pages.root_content.clone(),181 ROUTE_LOGS_PATH => config.pages.logs_content.clone(),182 ROUTE_CONFIG_PATH => config.pages.config_content.clone(),183 ROUTE_TOKENINFO_PATH => config.pages.tokeninfo_content.clone(),184 ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content.clone(),185 ROUTE_SHARED_JS_PATH => config.pages.shared_js_content.clone(),186 ROUTE_ABOUT_PATH => config.pages.about_content.clone(),187 ROUTE_README_PATH => config.pages.readme_content.clone(),188 ROUTE_API_PATH => config.pages.api_content.clone(),189 _ => PageContent::default(),190 })191 }192 193 pub fn get_usage_check() -> UsageCheck {194 APP_CONFIG195 .read()196 .map(|config| config.usage_check.clone())197 .unwrap_or_default()198 }199 200 pub fn update_vision_ability(new_ability: VisionAbility) -> Result<(), &'static str> {201 if let Ok(mut config) = APP_CONFIG.write() {202 config.vision_ability = new_ability;203 Ok(())204 } else {205 Err(ERR_UPDATE_CONFIG)206 }207 }208 209 pub fn update_page_content(path: &str, content: PageContent) -> Result<(), &'static str> {210 if let Ok(mut config) = APP_CONFIG.write() {211 match path {212 ROUTE_ROOT_PATH => config.pages.root_content = content,213 ROUTE_LOGS_PATH => config.pages.logs_content = content,214 ROUTE_CONFIG_PATH => config.pages.config_content = content,215 ROUTE_TOKENINFO_PATH => config.pages.tokeninfo_content = content,216 ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = content,217 ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = content,218 ROUTE_ABOUT_PATH => config.pages.about_content = content,219 ROUTE_README_PATH => config.pages.readme_content = content,220 ROUTE_API_PATH => config.pages.api_content = content,221 _ => return Err(ERR_INVALID_PATH),222 }223 Ok(())224 } else {225 Err(ERR_UPDATE_CONFIG)226 }227 }228 229 pub fn update_usage_check(rule: UsageCheck) -> Result<(), &'static str> {230 if let Ok(mut config) = APP_CONFIG.write() {231 config.usage_check = rule;232 Ok(())233 } else {234 Err(ERR_UPDATE_CONFIG)235 }236 }237 238 pub fn reset_vision_ability() -> Result<(), &'static str> {239 if let Ok(mut config) = APP_CONFIG.write() {240 config.vision_ability = VisionAbility::Base64;241 Ok(())242 } else {243 Err(ERR_RESET_CONFIG)244 }245 }246 247 pub fn reset_page_content(path: &str) -> Result<(), &'static str> {248 if let Ok(mut config) = APP_CONFIG.write() {249 match path {250 ROUTE_ROOT_PATH => config.pages.root_content = PageContent::default(),251 ROUTE_LOGS_PATH => config.pages.logs_content = PageContent::default(),252 ROUTE_CONFIG_PATH => config.pages.config_content = PageContent::default(),253 ROUTE_TOKENINFO_PATH => config.pages.tokeninfo_content = PageContent::default(),254 ROUTE_SHARED_STYLES_PATH => {255 config.pages.shared_styles_content = PageContent::default()256 }257 ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = PageContent::default(),258 ROUTE_ABOUT_PATH => config.pages.about_content = PageContent::default(),259 ROUTE_README_PATH => config.pages.readme_content = PageContent::default(),260 ROUTE_API_PATH => config.pages.api_content = PageContent::default(),261 _ => return Err(ERR_INVALID_PATH),262 }263 Ok(())264 } else {265 Err(ERR_RESET_CONFIG)266 }267 }268 269 pub fn reset_usage_check() -> Result<(), &'static str> {270 if let Ok(mut config) = APP_CONFIG.write() {271 config.usage_check = UsageCheck::default();272 Ok(())273 } else {274 Err(ERR_RESET_CONFIG)275 }276 }277}278 279impl AppState {280 pub fn new(token_infos: Vec<TokenInfo>) -> Self {281 Self {282 total_requests: 0,283 active_requests: 0,284 error_requests: 0,285 request_logs: Vec::new(),286 token_infos,287 }288 }289}290 291// 请求日志292#[derive(Serialize, Clone)]293pub struct RequestLog {294 pub id: u64,295 pub timestamp: chrono::DateTime<chrono::Local>,296 pub model: String,297 pub token_info: TokenInfo,298 #[serde(skip_serializing_if = "Option::is_none")]299 pub prompt: Option<String>,300 pub timing: TimingInfo,301 pub stream: bool,302 pub status: &'static str,303 #[serde(skip_serializing_if = "Option::is_none")]304 pub error: Option<String>,305}306 307#[derive(Serialize, Clone)]308pub struct TimingInfo {309 pub total: f64, // 总用时(秒)310 #[serde(skip_serializing_if = "Option::is_none")]311 pub first: Option<f64>, // 首字时间(秒)312}313 314// 聊天请求315#[derive(Deserialize)]316pub struct ChatRequest {317 pub model: String,318 pub messages: Vec<Message>,319 #[serde(default)]320 pub stream: bool,321}322 323// 用于存储 token 信息324#[derive(Serialize, Clone)]325pub struct TokenInfo {326 pub token: String,327 pub checksum: String,328 #[serde(skip_serializing_if = "Option::is_none")]329 pub profile: Option<TokenProfile>,330}331 332// TokenUpdateRequest 结构体333#[derive(Deserialize)]334pub struct TokenUpdateRequest {335 pub tokens: String,336 #[serde(default)]337 pub token_list: Option<String>,338}339 