mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 10:57:03 +08:00
Merge origin/main into fix/gemini-cli-v1internal
This commit is contained in:
@@ -662,10 +662,5 @@ fn admin_provider_oauth_decode_response_bytes(
|
||||
}
|
||||
|
||||
fn admin_provider_oauth_gateway_error_message(error: GatewayError) -> String {
|
||||
match error {
|
||||
GatewayError::UpstreamUnavailable { message, .. }
|
||||
| GatewayError::ControlUnavailable { message, .. }
|
||||
| GatewayError::Client { message, .. }
|
||||
| GatewayError::Internal(message) => message,
|
||||
}
|
||||
error.into_message()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use super::*;
|
||||
use aether_data_contracts::repository::provider_catalog::{
|
||||
StoredProviderCatalogKey, StoredProviderCatalogProvider,
|
||||
};
|
||||
use axum::{
|
||||
body::Body,
|
||||
http,
|
||||
@@ -193,8 +196,6 @@ impl<'a> AdminAppState<'a> {
|
||||
&self,
|
||||
provider_id: &str,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
use aether_admin::provider::pool as admin_provider_pool_pure;
|
||||
|
||||
let Some(provider) = self
|
||||
.read_provider_catalog_providers_by_ids(std::slice::from_ref(&provider_id.to_string()))
|
||||
.await?
|
||||
@@ -208,6 +209,30 @@ impl<'a> AdminAppState<'a> {
|
||||
.into_response());
|
||||
};
|
||||
|
||||
let affected = self
|
||||
.cleanup_known_banned_provider_catalog_keys(&provider)
|
||||
.await?;
|
||||
if affected == 0 {
|
||||
return Ok(Json(
|
||||
aether_admin::provider::pool::build_admin_pool_cleanup_empty_payload(
|
||||
"未发现可清理的异常账号",
|
||||
),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
Ok(
|
||||
Json(aether_admin::provider::pool::build_admin_pool_cleanup_result_payload(affected))
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_known_banned_provider_catalog_keys(
|
||||
&self,
|
||||
provider: &StoredProviderCatalogProvider,
|
||||
) -> Result<usize, GatewayError> {
|
||||
use aether_admin::provider::pool as admin_provider_pool_pure;
|
||||
|
||||
let banned_keys = self
|
||||
.list_provider_catalog_keys_by_provider_ids(std::slice::from_ref(&provider.id))
|
||||
.await?
|
||||
@@ -215,12 +240,7 @@ impl<'a> AdminAppState<'a> {
|
||||
.filter(admin_provider_pool_pure::admin_pool_key_is_known_banned)
|
||||
.collect::<Vec<_>>();
|
||||
if banned_keys.is_empty() {
|
||||
return Ok(Json(
|
||||
admin_provider_pool_pure::build_admin_pool_cleanup_empty_payload(
|
||||
"未发现可清理的异常账号",
|
||||
),
|
||||
)
|
||||
.into_response());
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let deleted_key_ids = banned_keys
|
||||
@@ -243,10 +263,42 @@ impl<'a> AdminAppState<'a> {
|
||||
self.cleanup_deleted_provider_catalog_refs(&provider.id, &[], &deleted_key_ids)
|
||||
.await?;
|
||||
|
||||
Ok(
|
||||
Json(admin_provider_pool_pure::build_admin_pool_cleanup_result_payload(affected))
|
||||
.into_response(),
|
||||
)
|
||||
Ok(affected)
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_provider_catalog_key_if_current<F>(
|
||||
&self,
|
||||
provider: &StoredProviderCatalogProvider,
|
||||
key_id: &str,
|
||||
should_delete: F,
|
||||
) -> Result<bool, GatewayError>
|
||||
where
|
||||
F: FnOnce(&StoredProviderCatalogKey) -> bool,
|
||||
{
|
||||
let key_ids = [key_id.to_string()];
|
||||
let Some(key) = self
|
||||
.read_provider_catalog_keys_by_ids(&key_ids)
|
||||
.await?
|
||||
.into_iter()
|
||||
.next()
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
if key.provider_id != provider.id || !should_delete(&key) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
self.clear_admin_provider_pool_cooldown(&provider.id, &key.id)
|
||||
.await;
|
||||
self.reset_admin_provider_pool_cost(&provider.id, &key.id)
|
||||
.await;
|
||||
let deleted = self.delete_provider_catalog_key(&key.id).await?;
|
||||
if deleted {
|
||||
let deleted_key_ids = [key.id.clone()];
|
||||
self.cleanup_deleted_provider_catalog_refs(&provider.id, &[], &deleted_key_ids)
|
||||
.await?;
|
||||
}
|
||||
Ok(deleted)
|
||||
}
|
||||
|
||||
pub(crate) async fn build_admin_pool_batch_action_response(
|
||||
|
||||
@@ -40,6 +40,7 @@ impl<'a> AdminAppState<'a> {
|
||||
.map(|model| AdminSystemConfigGlobalModel {
|
||||
name: model.name.clone(),
|
||||
display_name: model.display_name.clone(),
|
||||
usage_count: Some(model.usage_count),
|
||||
default_price_per_request: model.default_price_per_request,
|
||||
default_tiered_pricing: model.default_tiered_pricing.clone(),
|
||||
supported_capabilities: model.supported_capabilities.as_ref().and_then(|value| {
|
||||
@@ -169,6 +170,13 @@ impl<'a> AdminAppState<'a> {
|
||||
) -> Result<serde_json::Value, GatewayError> {
|
||||
let users = self.list_non_admin_export_users().await?;
|
||||
let user_ids = users.iter().map(|user| user.id.clone()).collect::<Vec<_>>();
|
||||
let user_usage_totals = self
|
||||
.app
|
||||
.summarize_usage_totals_by_user_ids(&user_ids)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|totals| (totals.user_id.clone(), totals))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let user_wallets = self.list_wallet_snapshots_by_user_ids(&user_ids).await?;
|
||||
let user_api_keys = self
|
||||
.list_auth_api_key_export_records_by_user_ids(&user_ids)
|
||||
@@ -185,6 +193,7 @@ impl<'a> AdminAppState<'a> {
|
||||
let standalone_wallets = self
|
||||
.list_wallet_snapshots_by_api_key_ids(&standalone_api_key_ids)
|
||||
.await?;
|
||||
let usage_aggregates = self.export_admin_system_usage_aggregates().await?;
|
||||
|
||||
let wallets_by_user_id = user_wallets
|
||||
.into_iter()
|
||||
@@ -260,8 +269,10 @@ impl<'a> AdminAppState<'a> {
|
||||
self.build_admin_system_users_export_api_key_payload(key, None, true)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let usage_totals = user_usage_totals.get(&user.id);
|
||||
|
||||
json!({
|
||||
"id": user.id.clone(),
|
||||
"email": user.email.clone(),
|
||||
"email_verified": user.email_verified,
|
||||
"username": user.username.clone(),
|
||||
@@ -284,6 +295,12 @@ impl<'a> AdminAppState<'a> {
|
||||
.unwrap_or(false),
|
||||
"wallet": wallet_payload,
|
||||
"is_active": user.is_active,
|
||||
"request_count": usage_totals
|
||||
.map(|totals| totals.request_count)
|
||||
.unwrap_or(0),
|
||||
"total_tokens": usage_totals
|
||||
.map(|totals| totals.total_tokens)
|
||||
.unwrap_or(0),
|
||||
"api_keys": api_keys_payload,
|
||||
})
|
||||
})
|
||||
@@ -306,6 +323,7 @@ impl<'a> AdminAppState<'a> {
|
||||
"user_groups": user_groups_data,
|
||||
"users": users_data,
|
||||
"standalone_keys": standalone_keys_data,
|
||||
"usage_aggregates": usage_aggregates,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -330,6 +348,7 @@ impl<'a> AdminAppState<'a> {
|
||||
include_is_standalone: bool,
|
||||
) -> serde_json::Value {
|
||||
let mut payload = serde_json::Map::from_iter([
|
||||
("api_key_id".to_string(), json!(key.api_key_id.clone())),
|
||||
("key_hash".to_string(), json!(key.key_hash.clone())),
|
||||
("name".to_string(), json!(key.name.clone())),
|
||||
(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,26 @@ impl<'a> AdminAppState<'a> {
|
||||
self.app.purge_admin_system_data(target).await
|
||||
}
|
||||
|
||||
pub(crate) async fn export_admin_system_usage_aggregates(
|
||||
&self,
|
||||
) -> Result<aether_data::repository::system::AdminSystemUsageAggregateSnapshot, GatewayError>
|
||||
{
|
||||
self.app.export_admin_system_usage_aggregates().await
|
||||
}
|
||||
|
||||
pub(crate) async fn import_admin_system_usage_aggregates(
|
||||
&self,
|
||||
snapshot: &aether_data::repository::system::AdminSystemUsageAggregateSnapshot,
|
||||
user_id_map: &std::collections::BTreeMap<String, String>,
|
||||
api_key_id_map: &std::collections::BTreeMap<String, String>,
|
||||
mode: aether_data::repository::system::AdminSystemUsageAggregateImportMode,
|
||||
) -> Result<aether_data::repository::system::AdminSystemUsageAggregateImportSummary, GatewayError>
|
||||
{
|
||||
self.app
|
||||
.import_admin_system_usage_aggregates(snapshot, user_id_map, api_key_id_map, mode)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn run_admin_system_cleanup_once(
|
||||
&self,
|
||||
) -> Result<crate::maintenance::AdminSystemCleanupSummary, GatewayError> {
|
||||
|
||||
@@ -706,6 +706,19 @@ impl<'a> AdminAppState<'a> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn set_api_key_usage_totals(
|
||||
&self,
|
||||
api_key_id: &str,
|
||||
total_requests: u64,
|
||||
total_tokens: u64,
|
||||
total_cost_usd: f64,
|
||||
) -> Result<Option<aether_data::repository::auth::StoredAuthApiKeyExportRecord>, GatewayError>
|
||||
{
|
||||
self.app
|
||||
.set_api_key_usage_totals(api_key_id, total_requests, total_tokens, total_cost_usd)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_user_api_key(
|
||||
&self,
|
||||
user_id: &str,
|
||||
|
||||
Reference in New Issue
Block a user