|
use super::constant::{ |
|
COMMA, CURSOR_API2_HOST, CURSOR_HOST, DEFAULT_TOKEN_LIST_FILE_NAME, EMPTY_STRING, |
|
}; |
|
use crate::common::utils::{ |
|
parse_ascii_char_from_env, parse_bool_from_env, parse_string_from_env, parse_usize_from_env, |
|
}; |
|
use std::sync::LazyLock; |
|
use tokio::sync::{Mutex, OnceCell}; |
|
|
|
macro_rules! def_pub_static { |
|
|
|
($name:ident, $value:expr) => { |
|
pub static $name: LazyLock<String> = LazyLock::new(|| $value); |
|
}; |
|
|
|
|
|
($name:ident, env: $env_key:expr, default: $default:expr) => { |
|
pub static $name: LazyLock<String> = |
|
LazyLock::new(|| parse_string_from_env($env_key, $default).trim().to_string()); |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def_pub_static!(ROUTE_PREFIX, env: "ROUTE_PREFIX", default: EMPTY_STRING); |
|
def_pub_static!(AUTH_TOKEN, env: "AUTH_TOKEN", default: EMPTY_STRING); |
|
def_pub_static!(TOKEN_LIST_FILE, env: "TOKEN_LIST_FILE", default: DEFAULT_TOKEN_LIST_FILE_NAME); |
|
def_pub_static!(ROUTE_MODELS_PATH, format!("{}/v1/models", *ROUTE_PREFIX)); |
|
def_pub_static!( |
|
ROUTE_CHAT_PATH, |
|
format!("{}/v1/chat/completions", *ROUTE_PREFIX) |
|
); |
|
|
|
pub static START_TIME: LazyLock<chrono::DateTime<chrono::Local>> = |
|
LazyLock::new(chrono::Local::now); |
|
|
|
pub fn get_start_time() -> chrono::DateTime<chrono::Local> { |
|
*START_TIME |
|
} |
|
|
|
def_pub_static!(DEFAULT_INSTRUCTIONS, env: "DEFAULT_INSTRUCTIONS", default: "Respond in Chinese by default"); |
|
|
|
def_pub_static!(REVERSE_PROXY_HOST, env: "REVERSE_PROXY_HOST", default: EMPTY_STRING); |
|
|
|
const DEFAULT_KEY_PREFIX: &str = "sk-"; |
|
|
|
pub static KEY_PREFIX: LazyLock<String> = LazyLock::new(|| { |
|
let value = parse_string_from_env("KEY_PREFIX", DEFAULT_KEY_PREFIX) |
|
.trim() |
|
.to_string(); |
|
if value.is_empty() { |
|
DEFAULT_KEY_PREFIX.to_string() |
|
} else { |
|
value |
|
} |
|
}); |
|
|
|
pub static KEY_PREFIX_LEN: LazyLock<usize> = LazyLock::new(|| KEY_PREFIX.len()); |
|
|
|
pub static TOKEN_DELIMITER: LazyLock<char> = LazyLock::new(|| { |
|
let delimiter = parse_ascii_char_from_env("TOKEN_DELIMITER", COMMA); |
|
if delimiter.is_ascii_alphabetic() |
|
|| delimiter.is_ascii_digit() |
|
|| delimiter == '+' |
|
|| delimiter == '/' |
|
{ |
|
COMMA |
|
} else { |
|
delimiter |
|
} |
|
}); |
|
|
|
pub static USE_COMMA_DELIMITER: LazyLock<bool> = LazyLock::new(|| { |
|
let enable = parse_bool_from_env("USE_COMMA_DELIMITER", true); |
|
if enable && *TOKEN_DELIMITER == COMMA { |
|
false |
|
} else { |
|
enable |
|
} |
|
}); |
|
|
|
pub static USE_REVERSE_PROXY: LazyLock<bool> = LazyLock::new(|| !REVERSE_PROXY_HOST.is_empty()); |
|
|
|
macro_rules! def_cursor_api_url { |
|
($name:ident, $api_host:expr, $path:expr) => { |
|
pub static $name: LazyLock<String> = LazyLock::new(|| { |
|
let host = if *USE_REVERSE_PROXY { |
|
&*REVERSE_PROXY_HOST |
|
} else { |
|
$api_host |
|
}; |
|
format!("https://{}{}", host, $path) |
|
}); |
|
}; |
|
} |
|
|
|
def_cursor_api_url!( |
|
CURSOR_API2_CHAT_URL, |
|
CURSOR_API2_HOST, |
|
"/aiserver.v1.AiService/StreamChat" |
|
); |
|
|
|
def_cursor_api_url!( |
|
CURSOR_API2_CHAT_WEB_URL, |
|
CURSOR_API2_HOST, |
|
"/aiserver.v1.AiService/StreamChatWeb" |
|
); |
|
|
|
def_cursor_api_url!( |
|
CURSOR_API2_STRIPE_URL, |
|
CURSOR_API2_HOST, |
|
"/auth/full_stripe_profile" |
|
); |
|
|
|
def_cursor_api_url!(CURSOR_USAGE_API_URL, CURSOR_HOST, "/api/usage"); |
|
|
|
def_cursor_api_url!(CURSOR_USER_API_URL, CURSOR_HOST, "/api/auth/me"); |
|
|
|
pub(super) static LOGS_FILE_PATH: LazyLock<String> = |
|
LazyLock::new(|| parse_string_from_env("LOGS_FILE_PATH", "logs.bin")); |
|
|
|
pub(super) static PAGES_FILE_PATH: LazyLock<String> = |
|
LazyLock::new(|| parse_string_from_env("PAGES_FILE_PATH", "pages.bin")); |
|
|
|
pub static DEBUG: LazyLock<bool> = LazyLock::new(|| parse_bool_from_env("DEBUG", false)); |
|
|
|
|
|
static DEBUG_LOG_FILE: LazyLock<String> = |
|
LazyLock::new(|| parse_string_from_env("DEBUG_LOG_FILE", "debug.log")); |
|
|
|
|
|
static LOG_FILE: OnceCell<Mutex<tokio::fs::File>> = OnceCell::const_new(); |
|
|
|
pub(crate) async fn get_log_file() -> &'static Mutex<tokio::fs::File> { |
|
LOG_FILE |
|
.get_or_init(|| async { |
|
Mutex::new( |
|
tokio::fs::OpenOptions::new() |
|
.create(true) |
|
.append(true) |
|
.open(&*DEBUG_LOG_FILE) |
|
.await |
|
.expect("无法打开日志文件"), |
|
) |
|
}) |
|
.await |
|
} |
|
|
|
#[macro_export] |
|
macro_rules! debug_println { |
|
($($arg:tt)*) => { |
|
if *crate::app::lazy::DEBUG { |
|
let time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); |
|
let log_message = format!("{} - {}", time, format!($($arg)*)); |
|
use tokio::io::AsyncWriteExt as _; |
|
|
|
|
|
tokio::spawn(async move { |
|
let log_file = crate::app::lazy::get_log_file().await; |
|
|
|
let mut file = log_file.lock().await; |
|
if let Err(err) = file.write_all(log_message.as_bytes()).await { |
|
eprintln!("写入日志文件失败: {}", err); |
|
} |
|
if let Err(err) = file.write_all(b"\n").await { |
|
eprintln!("写入换行符失败: {}", err); |
|
} |
|
|
|
|
|
}); |
|
} |
|
}; |
|
} |
|
|
|
pub static REQUEST_LOGS_LIMIT: LazyLock<usize> = |
|
LazyLock::new(|| std::cmp::min(parse_usize_from_env("REQUEST_LOGS_LIMIT", 100), 2000)); |
|
|
|
pub static SERVICE_TIMEOUT: LazyLock<u64> = LazyLock::new(|| { |
|
let timeout = parse_usize_from_env("SERVICE_TIMEOUT", 30); |
|
u64::try_from(timeout).map(|t| t.min(600)).unwrap_or(30) |
|
}); |
|
|