2026-04-09 00:10:38 +08:00
|
|
|
use super::state::{
|
|
|
|
|
enrich_admin_provider_oauth_auth_config, json_non_empty_string, json_u64_value,
|
|
|
|
|
};
|
|
|
|
|
use crate::handlers::admin::request::AdminAppState;
|
2026-04-23 14:42:51 +08:00
|
|
|
use crate::provider_key_auth::provider_active_api_formats;
|
2026-04-09 00:10:38 +08:00
|
|
|
use crate::GatewayError;
|
|
|
|
|
use aether_data_contracts::repository::provider_catalog::{
|
|
|
|
|
StoredProviderCatalogEndpoint, StoredProviderCatalogKey,
|
|
|
|
|
};
|
2026-04-23 14:42:51 +08:00
|
|
|
use aether_provider_transport::provider_types::provider_type_is_fixed;
|
2026-04-09 00:10:38 +08:00
|
|
|
use serde_json::json;
|
|
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
pub(crate) fn provider_oauth_key_proxy_value(
|
|
|
|
|
proxy_node_id: Option<&str>,
|
|
|
|
|
) -> Option<serde_json::Value> {
|
|
|
|
|
proxy_node_id
|
|
|
|
|
.map(str::trim)
|
|
|
|
|
.filter(|value| !value.is_empty())
|
|
|
|
|
.map(|value| json!({ "node_id": value, "enabled": true }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn provider_oauth_active_api_formats(
|
|
|
|
|
endpoints: &[StoredProviderCatalogEndpoint],
|
|
|
|
|
) -> Vec<String> {
|
2026-04-23 14:42:51 +08:00
|
|
|
provider_active_api_formats(endpoints)
|
2026-04-09 00:10:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn build_provider_oauth_auth_config_from_token_payload(
|
|
|
|
|
provider_type: &str,
|
|
|
|
|
token_payload: &serde_json::Value,
|
|
|
|
|
) -> (
|
|
|
|
|
serde_json::Map<String, serde_json::Value>,
|
|
|
|
|
Option<String>,
|
|
|
|
|
Option<String>,
|
|
|
|
|
Option<u64>,
|
|
|
|
|
) {
|
|
|
|
|
let access_token = json_non_empty_string(token_payload.get("access_token"));
|
|
|
|
|
let refresh_token = json_non_empty_string(token_payload.get("refresh_token"));
|
|
|
|
|
let now_unix_secs = SystemTime::now()
|
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|duration| duration.as_secs())
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
let expires_at = json_u64_value(token_payload.get("expires_in"))
|
|
|
|
|
.map(|expires_in| now_unix_secs.saturating_add(expires_in));
|
|
|
|
|
|
|
|
|
|
let mut auth_config = serde_json::Map::new();
|
|
|
|
|
auth_config.insert("provider_type".to_string(), json!(provider_type));
|
|
|
|
|
auth_config.insert("updated_at".to_string(), json!(now_unix_secs));
|
|
|
|
|
if let Some(token_type) = token_payload.get("token_type").cloned() {
|
|
|
|
|
auth_config.insert("token_type".to_string(), token_type);
|
|
|
|
|
}
|
|
|
|
|
if let Some(refresh_token) = refresh_token.as_ref() {
|
|
|
|
|
auth_config.insert("refresh_token".to_string(), json!(refresh_token));
|
|
|
|
|
}
|
|
|
|
|
if let Some(expires_at) = expires_at {
|
|
|
|
|
auth_config.insert("expires_at".to_string(), json!(expires_at));
|
|
|
|
|
}
|
|
|
|
|
if let Some(scope) = token_payload.get("scope").cloned() {
|
|
|
|
|
auth_config.insert("scope".to_string(), scope);
|
|
|
|
|
}
|
|
|
|
|
enrich_admin_provider_oauth_auth_config(provider_type, &mut auth_config, token_payload);
|
|
|
|
|
(auth_config, access_token, refresh_token, expires_at)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn create_provider_oauth_catalog_key(
|
|
|
|
|
state: &AdminAppState<'_>,
|
|
|
|
|
provider_id: &str,
|
2026-04-23 14:42:51 +08:00
|
|
|
provider_type: &str,
|
2026-04-09 00:10:38 +08:00
|
|
|
name: &str,
|
|
|
|
|
access_token: &str,
|
|
|
|
|
auth_config: &serde_json::Map<String, serde_json::Value>,
|
|
|
|
|
api_formats: &[String],
|
|
|
|
|
proxy: Option<serde_json::Value>,
|
|
|
|
|
expires_at_unix_secs: Option<u64>,
|
|
|
|
|
) -> Result<Option<StoredProviderCatalogKey>, GatewayError> {
|
|
|
|
|
let Some(encrypted_api_key) = state.encrypt_catalog_secret_with_fallbacks(access_token) else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let auth_config_json = serde_json::to_string(&serde_json::Value::Object(auth_config.clone()))
|
|
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
|
|
|
|
let Some(encrypted_auth_config) =
|
|
|
|
|
state.encrypt_catalog_secret_with_fallbacks(&auth_config_json)
|
|
|
|
|
else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let now_unix_secs = SystemTime::now()
|
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|duration| duration.as_secs())
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
let mut record = StoredProviderCatalogKey::new(
|
|
|
|
|
Uuid::new_v4().to_string(),
|
|
|
|
|
provider_id.to_string(),
|
|
|
|
|
name.to_string(),
|
|
|
|
|
"oauth".to_string(),
|
|
|
|
|
None,
|
|
|
|
|
true,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()))?
|
|
|
|
|
.with_transport_fields(
|
2026-04-23 14:42:51 +08:00
|
|
|
provider_oauth_catalog_key_api_formats(provider_type, api_formats),
|
2026-04-09 00:10:38 +08:00
|
|
|
encrypted_api_key,
|
|
|
|
|
Some(encrypted_auth_config),
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
expires_at_unix_secs,
|
|
|
|
|
proxy,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
|
|
|
|
record.internal_priority = 50;
|
|
|
|
|
record.cache_ttl_minutes = 5;
|
|
|
|
|
record.max_probe_interval_minutes = 32;
|
|
|
|
|
record.request_count = Some(0);
|
|
|
|
|
record.success_count = Some(0);
|
|
|
|
|
record.error_count = Some(0);
|
|
|
|
|
record.total_response_time_ms = Some(0);
|
|
|
|
|
record.health_by_format = Some(json!({}));
|
|
|
|
|
record.circuit_breaker_by_format = Some(json!({}));
|
2026-04-10 01:46:14 +08:00
|
|
|
record.created_at_unix_ms = Some(now_unix_secs);
|
2026-04-09 00:10:38 +08:00
|
|
|
record.updated_at_unix_secs = Some(now_unix_secs);
|
2026-04-27 22:08:02 +08:00
|
|
|
let created = state.create_provider_catalog_key(&record).await?;
|
|
|
|
|
if let Some(key) = created.as_ref() {
|
|
|
|
|
let _ = state
|
|
|
|
|
.app()
|
|
|
|
|
.invalidate_local_oauth_refresh_entry(&key.id)
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
Ok(created)
|
2026-04-09 00:10:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn update_existing_provider_oauth_catalog_key(
|
|
|
|
|
state: &AdminAppState<'_>,
|
|
|
|
|
existing_key: &StoredProviderCatalogKey,
|
2026-04-23 14:42:51 +08:00
|
|
|
provider_type: &str,
|
2026-04-09 00:10:38 +08:00
|
|
|
access_token: &str,
|
|
|
|
|
auth_config: &serde_json::Map<String, serde_json::Value>,
|
2026-04-23 14:42:51 +08:00
|
|
|
api_formats: &[String],
|
2026-04-09 00:10:38 +08:00
|
|
|
proxy: Option<serde_json::Value>,
|
|
|
|
|
expires_at_unix_secs: Option<u64>,
|
|
|
|
|
) -> Result<Option<StoredProviderCatalogKey>, GatewayError> {
|
|
|
|
|
let Some(encrypted_api_key) = state.encrypt_catalog_secret_with_fallbacks(access_token) else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let auth_config_json = serde_json::to_string(&serde_json::Value::Object(auth_config.clone()))
|
|
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
|
|
|
|
let Some(encrypted_auth_config) =
|
|
|
|
|
state.encrypt_catalog_secret_with_fallbacks(&auth_config_json)
|
|
|
|
|
else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let now_unix_secs = SystemTime::now()
|
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|duration| duration.as_secs())
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
let mut updated = existing_key.clone();
|
2026-04-29 15:46:50 +08:00
|
|
|
updated.encrypted_api_key = Some(encrypted_api_key);
|
2026-04-09 00:10:38 +08:00
|
|
|
updated.encrypted_auth_config = Some(encrypted_auth_config);
|
2026-04-23 14:42:51 +08:00
|
|
|
updated.api_formats = provider_oauth_catalog_key_api_formats(provider_type, api_formats);
|
2026-04-09 00:10:38 +08:00
|
|
|
updated.is_active = true;
|
|
|
|
|
updated.expires_at_unix_secs = expires_at_unix_secs;
|
|
|
|
|
updated.oauth_invalid_at_unix_secs = None;
|
|
|
|
|
updated.oauth_invalid_reason = None;
|
|
|
|
|
updated.health_by_format = Some(json!({}));
|
|
|
|
|
updated.circuit_breaker_by_format = Some(json!({}));
|
|
|
|
|
updated.error_count = Some(0);
|
|
|
|
|
if let Some(proxy) = proxy {
|
|
|
|
|
updated.proxy = Some(proxy);
|
|
|
|
|
}
|
|
|
|
|
updated.updated_at_unix_secs = Some(now_unix_secs);
|
2026-04-27 22:08:02 +08:00
|
|
|
let persisted = state.update_provider_catalog_key(&updated).await?;
|
|
|
|
|
if let Some(key) = persisted.as_ref() {
|
|
|
|
|
let _ = state
|
|
|
|
|
.app()
|
|
|
|
|
.invalidate_local_oauth_refresh_entry(&key.id)
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
Ok(persisted)
|
2026-04-09 00:10:38 +08:00
|
|
|
}
|
2026-04-23 14:42:51 +08:00
|
|
|
|
|
|
|
|
fn provider_oauth_catalog_key_api_formats(
|
|
|
|
|
provider_type: &str,
|
|
|
|
|
api_formats: &[String],
|
|
|
|
|
) -> Option<serde_json::Value> {
|
|
|
|
|
if provider_type_is_fixed(provider_type) {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(json!(api_formats))
|
|
|
|
|
}
|
|
|
|
|
}
|