File size: 6,214 Bytes
3c2af29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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 {
    // 基础版本:直接存储 String
    ($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());
    };
}

// macro_rules! def_pub_static_getter {
//     ($name:ident) => {
//         paste::paste! {
//             pub fn [<get_ $name:lower>]() -> String {
//                 (*$name).clone()
//             }
//         }
//     };
// }

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));

// 使用环境变量 "DEBUG_LOG_FILE" 来指定日志文件路径,默认值为 "debug.log"
static DEBUG_LOG_FILE: LazyLock<String> =
    LazyLock::new(|| parse_string_from_env("DEBUG_LOG_FILE", "debug.log"));

// 使用 OnceCell 结合 Mutex 来异步初始化 LOG_FILE
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 在后台异步写入日志
            tokio::spawn(async move {
                let log_file = crate::app::lazy::get_log_file().await;
                // 使用 MutexGuard 获取可变引用
                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);
                }
                // 可以选择在写入失败时 panic,或者忽略
                // panic!("写入日志文件失败: {}", 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)
});