feat(gateway): 重构 usage 数据层、迁移系统与系统导入

数据库迁移:
- 引入 baseline v2 bootstrap,空库首次启动自动初始化
- 服务启动不再自动执行迁移,需显式 `--migrate` 运行
- 新增 pending migration 检测,schema 落后时拒绝启动

Usage 数据层:
- usage body 存储外部化为独立 blob 表
- 新增 HTTP audit 表拆分存储请求/响应头与 body ref
- 后台清理任务支持 legacy body ref 元数据迁移
- usage runtime 写入迁移到专用 tokio runtime(独立线程池, 8MB 栈)

系统导入/导出:
- 支持用户、API Keys、钱包数据的完整导入
- 兼容 legacy 与 v1.3+ 两种导出格式

其他改进:
- executor outcome 增加 runtime miss 诊断上下文
- 主 tokio runtime 栈大小调整为 8MB
- 前端 provider 管理支持 base URL 配置
- dev.sh 支持 --migrate 参数
This commit is contained in:
fawney19
2026-04-13 14:01:22 +08:00
parent 3698e5a833
commit 5bb08e6aa4
106 changed files with 21730 additions and 1523 deletions
@@ -84,6 +84,10 @@ impl<'a> AdminAppState<'a> {
self.app.has_user_data_reader()
}
pub(crate) fn has_auth_user_data_reader(&self) -> bool {
self.app.has_auth_user_data_reader()
}
pub(crate) fn has_proxy_node_reader(&self) -> bool {
self.app.has_proxy_node_reader()
}
@@ -54,6 +54,17 @@ impl<'a> AdminAppState<'a> {
.map_err(|err| GatewayError::Internal(err.to_string()))
}
pub(crate) async fn resolve_request_usage_body_ref(
&self,
body_ref: &str,
) -> Result<Option<serde_json::Value>, GatewayError> {
self.app
.data
.resolve_request_usage_body_ref(body_ref)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))
}
pub(crate) async fn list_admin_usage_for_range(
&self,
time_range: &crate::handlers::admin::observability::AdminStatsTimeRange,
File diff suppressed because it is too large Load Diff
@@ -16,6 +16,16 @@ impl<'a> AdminAppState<'a> {
self.app.list_users_by_ids(user_ids).await
}
pub(crate) async fn resolve_auth_user_summaries_by_ids(
&self,
user_ids: &[String],
) -> Result<
std::collections::BTreeMap<String, aether_data::repository::users::StoredUserSummary>,
GatewayError,
> {
self.app.resolve_auth_user_summaries_by_ids(user_ids).await
}
pub(crate) async fn list_export_users_page(
&self,
query: &aether_data::repository::users::UserExportListQuery,
@@ -103,6 +113,17 @@ impl<'a> AdminAppState<'a> {
.await
}
pub(crate) async fn initialize_auth_api_key_wallet(
&self,
api_key_id: &str,
initial_gift_usd: f64,
unlimited: bool,
) -> Result<Option<aether_data::repository::wallet::StoredWalletSnapshot>, GatewayError> {
self.app
.initialize_auth_api_key_wallet(api_key_id, initial_gift_usd, unlimited)
.await
}
pub(crate) async fn update_local_auth_user_profile(
&self,
user_id: &str,
@@ -165,10 +186,94 @@ impl<'a> AdminAppState<'a> {
.await
}
pub(crate) async fn update_auth_api_key_wallet_limit_mode(
&self,
api_key_id: &str,
limit_mode: &str,
) -> Result<Option<aether_data::repository::wallet::StoredWalletSnapshot>, GatewayError> {
self.app
.update_auth_api_key_wallet_limit_mode(api_key_id, limit_mode)
.await
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn update_auth_user_wallet_snapshot(
&self,
user_id: &str,
balance: f64,
gift_balance: f64,
limit_mode: &str,
currency: &str,
status: &str,
total_recharged: f64,
total_consumed: f64,
total_refunded: f64,
total_adjusted: f64,
updated_at_unix_secs: Option<u64>,
) -> Result<Option<aether_data::repository::wallet::StoredWalletSnapshot>, GatewayError> {
self.app
.update_auth_user_wallet_snapshot(
user_id,
balance,
gift_balance,
limit_mode,
currency,
status,
total_recharged,
total_consumed,
total_refunded,
total_adjusted,
updated_at_unix_secs,
)
.await
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn update_auth_api_key_wallet_snapshot(
&self,
api_key_id: &str,
balance: f64,
gift_balance: f64,
limit_mode: &str,
currency: &str,
status: &str,
total_recharged: f64,
total_consumed: f64,
total_refunded: f64,
total_adjusted: f64,
updated_at_unix_secs: Option<u64>,
) -> Result<Option<aether_data::repository::wallet::StoredWalletSnapshot>, GatewayError> {
self.app
.update_auth_api_key_wallet_snapshot(
api_key_id,
balance,
gift_balance,
limit_mode,
currency,
status,
total_recharged,
total_consumed,
total_refunded,
total_adjusted,
updated_at_unix_secs,
)
.await
}
pub(crate) async fn count_active_admin_users(&self) -> Result<u64, GatewayError> {
self.app.count_active_admin_users().await
}
pub(crate) async fn update_user_model_capability_settings(
&self,
user_id: &str,
settings: Option<serde_json::Value>,
) -> Result<Option<serde_json::Value>, GatewayError> {
self.app
.update_user_model_capability_settings(user_id, settings)
.await
}
pub(crate) async fn count_user_pending_refunds(
&self,
user_id: &str,
@@ -236,6 +341,24 @@ impl<'a> AdminAppState<'a> {
.map_err(|err| GatewayError::Internal(err.to_string()))
}
pub(crate) async fn resolve_auth_api_key_snapshots_by_ids(
&self,
api_key_ids: &[String],
) -> Result<Vec<aether_data::repository::auth::StoredAuthApiKeySnapshot>, GatewayError> {
self.app
.resolve_auth_api_key_snapshots_by_ids(api_key_ids)
.await
}
pub(crate) async fn resolve_auth_api_key_names_by_ids(
&self,
api_key_ids: &[String],
) -> Result<std::collections::BTreeMap<String, String>, GatewayError> {
self.app
.resolve_auth_api_key_names_by_ids(api_key_ids)
.await
}
pub(crate) async fn list_auth_api_key_export_records_by_user_ids(
&self,
user_ids: &[String],
@@ -339,6 +462,18 @@ impl<'a> AdminAppState<'a> {
.await
}
pub(crate) async fn set_user_api_key_active(
&self,
user_id: &str,
api_key_id: &str,
is_active: bool,
) -> Result<Option<aether_data::repository::auth::StoredAuthApiKeyExportRecord>, GatewayError>
{
self.app
.set_user_api_key_active(user_id, api_key_id, is_active)
.await
}
pub(crate) async fn set_user_api_key_locked(
&self,
user_id: &str,
@@ -362,6 +497,18 @@ impl<'a> AdminAppState<'a> {
.await
}
pub(crate) async fn set_user_api_key_force_capabilities(
&self,
user_id: &str,
api_key_id: &str,
force_capabilities: Option<serde_json::Value>,
) -> Result<Option<aether_data::repository::auth::StoredAuthApiKeyExportRecord>, GatewayError>
{
self.app
.set_user_api_key_force_capabilities(user_id, api_key_id, force_capabilities)
.await
}
pub(crate) async fn delete_user_api_key(
&self,
user_id: &str,