cror / src /main.rs
xcolorday's picture
Upload 442 files
6b356c4 verified
Raw
History Blame Contribute Delete
16.4 kB
#![allow(internal_features)]
#![feature(
addr_parse_ascii,
cold_path,
hasher_prefixfree_extras,
const_trait_impl,
const_default,
core_intrinsics,
associated_type_defaults,
sized_type_properties
)]
#![allow(clippy::redundant_static_lifetimes)]
#[macro_use]
extern crate cursor_api;
mod app;
mod common;
mod core;
mod leak;
mod natural_args;
use ::axum::{
Router, middleware,
routing::{get, post},
};
use ::tokio::signal;
use ::tower_http::{cors::CorsLayer, limit::RequestBodyLimitLayer};
use app::{
config::handle_config_update,
constant::{
EMPTY_STRING, EXE_NAME, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BUILD_KEY_PATH,
ROUTE_CONFIG_PATH, ROUTE_CONFIG_VERSION_GET_PATH, ROUTE_CPP_CONFIG_PATH,
ROUTE_CPP_MODELS_PATH, ROUTE_CPP_STREAM_PATH, ROUTE_ENV_EXAMPLE_PATH, ROUTE_FILE_SYNC_PATH,
ROUTE_FILE_UPLOAD_PATH, ROUTE_GEN_CHECKSUM, ROUTE_GEN_HASH, ROUTE_GEN_UUID,
ROUTE_GET_TIMESTAMP_HEADER, ROUTE_HEALTH_PATH, ROUTE_LOGS_GET_PATH, ROUTE_LOGS_PATH,
ROUTE_LOGS_TOKENS_GET_PATH, ROUTE_PROXIES_ADD_PATH, ROUTE_PROXIES_DELETE_PATH,
ROUTE_PROXIES_GET_PATH, ROUTE_PROXIES_PATH, ROUTE_PROXIES_SET_GENERAL_PATH,
ROUTE_PROXIES_SET_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH, ROUTE_STATIC_PATH,
ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_ALIAS_SET_PATH,
ROUTE_TOKENS_CONFIG_VERSION_UPDATE_PATH, ROUTE_TOKENS_DELETE_PATH, ROUTE_TOKENS_GET_PATH,
ROUTE_TOKENS_PATH, ROUTE_TOKENS_PROFILE_UPDATE_PATH, ROUTE_TOKENS_PROXY_SET_PATH,
ROUTE_TOKENS_REFRESH_PATH, ROUTE_TOKENS_SET_PATH, ROUTE_TOKENS_STATUS_SET_PATH,
ROUTE_TOKENS_TIMEZONE_SET_PATH, VERSION,
},
lazy::AUTH_TOKEN,
model::{AppConfig, AppState},
};
use common::utils::parse_from_env;
use core::{
middleware::{admin_auth_middleware, cpp_auth_middleware, v1_auth_middleware},
route::{
handle_about, handle_add_proxy, handle_add_tokens, handle_api_page, handle_build_key,
handle_build_key_page, handle_config_page, handle_delete_proxies, handle_delete_tokens,
handle_env_example, handle_gen_checksum, handle_gen_hash, handle_gen_uuid,
handle_get_config_version, handle_get_logs, handle_get_logs_tokens, handle_get_proxies,
handle_get_timestamp_header, handle_get_tokens, handle_health, handle_logs, handle_options,
handle_proxies_page, handle_readme, handle_refresh_tokens, handle_root,
handle_set_general_proxy, handle_set_proxies, handle_set_tokens, handle_set_tokens_alias,
handle_set_tokens_proxy, handle_set_tokens_status, handle_set_tokens_timezone,
handle_static, handle_tokens_page, handle_update_tokens_config_version,
handle_update_tokens_profile,
},
service::{
cpp::{
handle_cpp_config, handle_cpp_models, handle_stream_cpp, handle_sync_file,
handle_upload_file,
},
handle_chat_completions, handle_messages, handle_models, handle_raw_models,
},
};
use natural_args::{DEFAULT_LISTEN_HOST, ENV_HOST, ENV_PORT};
#[tokio::main]
async fn main() {
// 设置自定义 panic hook
#[cfg(not(debug_assertions))]
::std::panic::set_hook(Box::new(|info| {
__cold_path!(); // panic 是异常路径
// std::env::set_var("RUST_BACKTRACE", "1");
if let Some(msg) = info.payload().downcast_ref::<String>() {
__eprintln!(msg);
} else if let Some(msg) = info.payload().downcast_ref::<&str>() {
__eprintln!(msg);
}
}));
// 加载环境变量
{
let current_exe = __unwrap_panic!(std::env::current_exe());
let file_name = __unwrap_panic!(
current_exe
.file_name()
.and_then(|s| s.to_str())
.ok_or("filename")
);
let expect = __unwrap_panic!(current_exe.parent().ok_or("parent"))
.join(EXE_NAME)
.is_file();
if file_name != EXE_NAME {
if expect {
println!(
"Oh, I see you already have a {EXE_NAME} sitting there. Multiple versions? How adventurous of you!"
)
} else {
println!("{file_name}? Really? *{EXE_NAME}* was literally right there!");
};
}
// 处理自然语言参数
natural_args::process_args(file_name);
}
// tracing_subscriber::fmt::init();
if AUTH_TOKEN.is_empty() {
__cold_path!();
__eprintln!("AUTH_TOKEN must be set\n");
std::process::exit(1);
};
// 初始化全局配置
AppConfig::init();
// 初始化应用状态
let state = std::sync::Arc::new(__unwrap_panic!(AppState::load().await));
// 尝试加载保存的配置
if let Err(e) = AppConfig::load() {
__cold_path!(); // 配置加载失败是错误路径
eprintln!("加载保存的配置失败: {e}");
}
// 创建一个克隆用于后台任务
let state_for_reload = state.clone();
// 启动后台任务在每个整1000秒时更新 checksum
tokio::spawn(async move {
use crate::app::model::TimestampHeader;
let state = state_for_reload;
let mut counter = 29u8;
loop {
let now = common::utils::now_secs();
let current_kilo = now / 1000;
// 更新为当前千秒
TimestampHeader::update_global_with(current_kilo);
// 等待到下一个千秒
let wait_duration = (current_kilo + 1) * 1000 - now;
::tokio::time::sleep(::core::time::Duration::from_secs(wait_duration)).await;
// 每30次循环才更新一次client_key
counter += 1;
if counter >= 30 {
state.update_client_key().await;
counter = 0;
}
}
});
// 创建一个克隆用于信号处理
let state_for_shutdown = state.clone();
// 设置关闭信号处理
let shutdown_signal = async move {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
{
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
#[cfg(not(unix))]
{
ctrl_c.await;
}
__println!("正在关闭服务器...");
// 保存配置
if let Err(e) = AppConfig::save() {
__cold_path!(); // 配置保存失败是错误路径
eprintln!("保存配置失败: {e}");
} else {
__println!("配置已保存");
}
// 保存状态
if let Err(e) = state_for_shutdown.save().await {
__cold_path!(); // 状态保存失败是错误路径
eprintln!("保存状态失败: {e}");
} else {
__println!("状态已保存");
}
app::lazy::log::flush_all_debug_logs().await;
};
// 设置路由
let app = {
let (
route_raw_models_path,
route_models_path,
route_chat_completions_path,
route_messages_path,
) = {
define_typed_constants! {
&'static str => {
RAW_MODELS_PATH = "/raw/models",
MODELS_PATH = "/v1/models",
CHAT_COMPLETIONS_PATH = "/v1/chat/completions",
MESSAGES_PATH = "/v1/messages",
}
}
use ::std::borrow::Cow;
let route_prefix = parse_from_env("ROUTE_PREFIX", EMPTY_STRING);
if route_prefix.is_empty() {
(
Cow::Borrowed(RAW_MODELS_PATH),
Cow::Borrowed(MODELS_PATH),
Cow::Borrowed(CHAT_COMPLETIONS_PATH),
Cow::Borrowed(MESSAGES_PATH),
)
} else {
#[inline]
fn make_route(route_prefix: &str, path: &'static str) -> Cow<'static, str> {
let mut route_path = String::with_capacity(path.len() + route_prefix.len());
route_path.push_str(route_prefix);
route_path.push_str(path);
Cow::Owned(route_path)
}
(
make_route(&route_prefix, RAW_MODELS_PATH),
make_route(&route_prefix, MODELS_PATH),
make_route(&route_prefix, CHAT_COMPLETIONS_PATH),
make_route(&route_prefix, MESSAGES_PATH),
)
}
};
Router::new()
.without_v07_checks()
.route(ROUTE_ROOT_PATH, get(handle_root))
.route(ROUTE_HEALTH_PATH, get(handle_health))
.route(ROUTE_TOKENS_PATH, get(handle_tokens_page))
.route(ROUTE_PROXIES_PATH, get(handle_proxies_page))
.merge(
Router::new()
.without_v07_checks()
.route(ROUTE_TOKENS_GET_PATH, post(handle_get_tokens))
.route(ROUTE_TOKENS_SET_PATH, post(handle_set_tokens))
.route(ROUTE_TOKENS_ADD_PATH, post(handle_add_tokens))
.route(ROUTE_TOKENS_DELETE_PATH, post(handle_delete_tokens))
.route(ROUTE_TOKENS_ALIAS_SET_PATH, post(handle_set_tokens_alias))
.route(
ROUTE_TOKENS_PROFILE_UPDATE_PATH,
post(handle_update_tokens_profile),
)
.route(
ROUTE_TOKENS_CONFIG_VERSION_UPDATE_PATH,
post(handle_update_tokens_config_version),
)
.route(ROUTE_TOKENS_REFRESH_PATH, post(handle_refresh_tokens))
.route(ROUTE_TOKENS_STATUS_SET_PATH, post(handle_set_tokens_status))
.route(ROUTE_TOKENS_PROXY_SET_PATH, post(handle_set_tokens_proxy))
.route(
ROUTE_TOKENS_TIMEZONE_SET_PATH,
post(handle_set_tokens_timezone),
)
.route(ROUTE_PROXIES_GET_PATH, post(handle_get_proxies))
.route(ROUTE_PROXIES_SET_PATH, post(handle_set_proxies))
.route(ROUTE_PROXIES_ADD_PATH, post(handle_add_proxy))
.route(ROUTE_PROXIES_DELETE_PATH, post(handle_delete_proxies))
.route(
ROUTE_PROXIES_SET_GENERAL_PATH,
post(handle_set_general_proxy),
)
.route_layer(middleware::from_fn(admin_auth_middleware)),
)
.merge(
Router::new()
.without_v07_checks()
.route(ROUTE_CPP_CONFIG_PATH, post(handle_cpp_config))
.route(ROUTE_CPP_MODELS_PATH, post(handle_cpp_models))
.route(ROUTE_FILE_UPLOAD_PATH, post(handle_upload_file))
.route(ROUTE_FILE_SYNC_PATH, post(handle_sync_file))
.route(ROUTE_CPP_STREAM_PATH, post(handle_stream_cpp))
.route_layer(middleware::from_fn_with_state(
state.clone(),
cpp_auth_middleware,
)),
)
.route(&route_raw_models_path, get(handle_raw_models))
.route(
&route_models_path,
get(handle_models).options(handle_options),
)
.route(
&route_messages_path,
post(handle_messages)
.route_layer(middleware::from_fn_with_state(
state.clone(),
v1_auth_middleware,
))
.options(handle_options),
)
.route(
&route_chat_completions_path,
post(handle_chat_completions)
.route_layer(middleware::from_fn_with_state(
state.clone(),
v1_auth_middleware,
))
.options(handle_options),
)
.route(ROUTE_LOGS_PATH, get(handle_logs))
.route(ROUTE_LOGS_GET_PATH, post(handle_get_logs))
.route(ROUTE_LOGS_TOKENS_GET_PATH, post(handle_get_logs_tokens))
.route(ROUTE_ENV_EXAMPLE_PATH, get(handle_env_example))
.route(
ROUTE_CONFIG_PATH,
get(handle_config_page).post(handle_config_update),
)
.route(
ROUTE_STATIC_PATH,
get(handle_static).options(handle_options),
)
.route(ROUTE_ABOUT_PATH, get(handle_about))
.route(ROUTE_README_PATH, get(handle_readme))
.route(ROUTE_API_PATH, get(handle_api_page))
.route(ROUTE_GEN_UUID, get(handle_gen_uuid))
.route(ROUTE_GEN_HASH, get(handle_gen_hash))
.route(ROUTE_GEN_CHECKSUM, get(handle_gen_checksum))
.route(ROUTE_GET_TIMESTAMP_HEADER, get(handle_get_timestamp_header))
// .route(ROUTE_BASIC_CALIBRATION_PATH, post(handle_basic_calibration))
// .route(ROUTE_USER_INFO_PATH, post(handle_user_info))
.route(
ROUTE_BUILD_KEY_PATH,
get(handle_build_key_page).post(handle_build_key),
)
.route(
ROUTE_CONFIG_VERSION_GET_PATH,
post(handle_get_config_version),
)
// .route(ROUTE_TOKEN_UPGRADE_PATH, post(handle_token_upgrade))
.layer(RequestBodyLimitLayer::new(parse_from_env(
"REQUEST_BODY_LIMIT",
2_000_000,
)))
.layer(CorsLayer::permissive())
.with_state(state)
};
// 启动服务器
let listener = {
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
let port = {
std::env::var(ENV_PORT)
.ok()
.and_then(|v| v.trim().parse().ok())
.unwrap_or(3000)
};
let addr = SocketAddr::new(
IpAddr::parse_ascii(parse_from_env(ENV_HOST, DEFAULT_LISTEN_HOST).as_bytes())
.unwrap_or_else(|e| {
__cold_path!(); // IP解析失败是错误路径
eprintln!("无法解析IP: {e}");
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}),
port,
);
println!("服务器运行在 {addr}");
tokio::net::TcpListener::bind(addr)
.await
.unwrap_or_else(|e| {
__cold_path!();
eprintln!("无法绑定到地址 {addr}: {e}");
std::process::exit(1);
})
};
println!("当前版本: v{VERSION}");
#[cfg(feature = "__preview")]
{
__println!("当前是测试版,有问题及时反馈哦~");
}
common::time::print_project_age();
common::time::print_build_age();
let start_time = app::lazy::get_start_time();
let server = axum::serve(listener, app);
tokio::select! {
result = server => {
if let Err(e) = result {
__cold_path!(); // 服务器错误是异常路径
eprintln!("服务器错误: {e}");
}
}
_ = shutdown_signal => {
println!(
"运行时间: {}",
common::utils::duration_fmt::human(__unwrap!(
app::model::DateTime::naive_now()
.signed_duration_since(*start_time)
.to_std()
))
.format(common::utils::duration_fmt::DurationFormat::Random)
.language(common::utils::duration_fmt::Language::Random)
);
common::time::print_project_age();
common::time::print_build_age();
__println!("服务器已关闭");
}
}
}