mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 19:29:50 +08:00
feat(admin): 完善代理节点与 OAuth 授权管理
This commit is contained in:
@@ -601,7 +601,14 @@ impl<'a> AdminAppState<'a> {
|
||||
client_api_format: "provider_oauth:exchange".to_string(),
|
||||
provider_api_format: "provider_oauth:exchange".to_string(),
|
||||
model_name: Some("oauth-exchange".to_string()),
|
||||
proxy: self.resolve_admin_proxy_node_snapshot(proxy_node_id).await,
|
||||
proxy: if proxy_node_id
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
{
|
||||
self.resolve_admin_proxy_node_snapshot(proxy_node_id).await
|
||||
} else {
|
||||
self.app.resolve_system_proxy_snapshot().await
|
||||
},
|
||||
tls_profile: None,
|
||||
timeouts: Some(ExecutionTimeouts {
|
||||
connect_ms: Some(ADMIN_PROVIDER_OAUTH_TIMEOUT_MS),
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
use super::*;
|
||||
use aether_contracts::ProxySnapshot;
|
||||
use aether_data::repository::proxy_nodes::{proxy_node_accepts_new_tunnels, StoredProxyNode};
|
||||
use aether_provider_transport::TransportTunnelAffinityLookup;
|
||||
use serde_json::{json, Map, Value};
|
||||
use serde_json::{Map, Value};
|
||||
use url::Url;
|
||||
|
||||
const TUNNEL_BASE_URL_EXTRA_KEY: &str = "tunnel_base_url";
|
||||
const TUNNEL_OWNER_INSTANCE_ID_EXTRA_KEY: &str = "tunnel_owner_instance_id";
|
||||
const TUNNEL_OWNER_OBSERVED_AT_EXTRA_KEY: &str = "tunnel_owner_observed_at_unix_secs";
|
||||
|
||||
impl<'a> AdminAppState<'a> {
|
||||
pub(crate) async fn read_provider_transport_snapshot(
|
||||
&self,
|
||||
@@ -83,10 +77,9 @@ impl<'a> AdminAppState<'a> {
|
||||
&self,
|
||||
transport: &AdminGatewayProviderTransportSnapshot,
|
||||
) -> Option<aether_contracts::ProxySnapshot> {
|
||||
crate::provider_transport::resolve_transport_proxy_snapshot_with_tunnel_affinity(
|
||||
self.app, transport,
|
||||
)
|
||||
.await
|
||||
self.app
|
||||
.resolve_transport_proxy_snapshot_with_tunnel_affinity(transport)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) fn fixed_provider_template(
|
||||
@@ -123,17 +116,7 @@ impl<'a> AdminAppState<'a> {
|
||||
}
|
||||
|
||||
if explicit_node_id.is_none() {
|
||||
let system_node_id = self
|
||||
.read_system_config_json_value("system_proxy_node_id")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|value| value.as_str().map(str::trim).map(ToOwned::to_owned))
|
||||
.filter(|value| !value.is_empty());
|
||||
if let Some(snapshot) = self
|
||||
.resolve_admin_proxy_node_snapshot(system_node_id.as_deref())
|
||||
.await
|
||||
{
|
||||
if let Some(snapshot) = self.app.resolve_system_proxy_snapshot().await {
|
||||
return Some(snapshot);
|
||||
}
|
||||
}
|
||||
@@ -147,60 +130,7 @@ impl<'a> AdminAppState<'a> {
|
||||
&self,
|
||||
node_id: Option<&str>,
|
||||
) -> Option<ProxySnapshot> {
|
||||
let node_id = node_id.map(str::trim).filter(|value| !value.is_empty())?;
|
||||
let node = self.find_proxy_node(node_id).await.ok().flatten()?;
|
||||
if node.status.trim() != "online" {
|
||||
return None;
|
||||
}
|
||||
if !proxy_node_accepts_new_tunnels(&node) {
|
||||
return None;
|
||||
}
|
||||
if node.tunnel_mode && node.tunnel_connected {
|
||||
let mut extra = Map::new();
|
||||
if let Ok(Some(owner)) = self.app().lookup_tunnel_attachment_owner(node_id).await {
|
||||
extra.insert(
|
||||
TUNNEL_BASE_URL_EXTRA_KEY.to_string(),
|
||||
Value::String(owner.relay_base_url),
|
||||
);
|
||||
extra.insert(
|
||||
TUNNEL_OWNER_INSTANCE_ID_EXTRA_KEY.to_string(),
|
||||
Value::String(owner.gateway_instance_id),
|
||||
);
|
||||
extra.insert(
|
||||
TUNNEL_OWNER_OBSERVED_AT_EXTRA_KEY.to_string(),
|
||||
json!(owner.observed_at_unix_secs),
|
||||
);
|
||||
}
|
||||
return Some(ProxySnapshot {
|
||||
enabled: Some(true),
|
||||
mode: Some("tunnel".to_string()),
|
||||
node_id: Some(node_id.to_string()),
|
||||
label: Some(node.name),
|
||||
url: None,
|
||||
extra: if extra.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Value::Object(extra))
|
||||
},
|
||||
});
|
||||
}
|
||||
if !node.is_manual {
|
||||
return None;
|
||||
}
|
||||
let proxy_url = node
|
||||
.proxy_url
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())?;
|
||||
Some(ProxySnapshot {
|
||||
enabled: Some(true),
|
||||
mode: admin_provider_transport_proxy_mode(Some(proxy_url)),
|
||||
node_id: Some(node.id.clone()),
|
||||
label: Some(node.name.clone()),
|
||||
url: admin_provider_transport_proxy_url_with_node_auth(&node)
|
||||
.or_else(|| Some(proxy_url.to_string())),
|
||||
extra: None,
|
||||
})
|
||||
self.app.resolve_proxy_node_snapshot(node_id).await
|
||||
}
|
||||
|
||||
pub(crate) fn supports_local_gemini_transport_with_network(
|
||||
@@ -387,25 +317,6 @@ fn admin_provider_transport_legacy_proxy_snapshot(value: &Value) -> Option<Proxy
|
||||
}
|
||||
}
|
||||
|
||||
fn admin_provider_transport_proxy_url_with_node_auth(node: &StoredProxyNode) -> Option<String> {
|
||||
let proxy_url = node
|
||||
.proxy_url
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())?;
|
||||
let username = node
|
||||
.proxy_username
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
let password = node
|
||||
.proxy_password
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
admin_provider_transport_inject_proxy_auth(proxy_url, username, password)
|
||||
}
|
||||
|
||||
fn admin_provider_transport_inject_proxy_auth(
|
||||
proxy_url: &str,
|
||||
username: Option<&str>,
|
||||
|
||||
@@ -2113,10 +2113,15 @@ impl<'a> AdminAppState<'a> {
|
||||
aether_data::repository::auth::UpdateStandaloneApiKeyBasicRecord {
|
||||
api_key_id: existing_key.api_key_id.clone(),
|
||||
name: name.clone(),
|
||||
rate_limit_present: true,
|
||||
rate_limit: Some(rate_limit),
|
||||
allowed_providers: Some(allowed_providers.clone()),
|
||||
allowed_api_formats: Some(allowed_api_formats.clone()),
|
||||
allowed_models: Some(allowed_models.clone()),
|
||||
expires_at_present: false,
|
||||
expires_at_unix_secs: None,
|
||||
auto_delete_on_expiry_present: false,
|
||||
auto_delete_on_expiry: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
@@ -2165,7 +2170,7 @@ impl<'a> AdminAppState<'a> {
|
||||
allowed_providers,
|
||||
allowed_api_formats,
|
||||
allowed_models,
|
||||
rate_limit,
|
||||
rate_limit: Some(rate_limit),
|
||||
concurrent_limit,
|
||||
force_capabilities,
|
||||
is_active,
|
||||
|
||||
@@ -11,6 +11,20 @@ use aether_admin::system::{
|
||||
use axum::{body::Body, response::Response};
|
||||
|
||||
impl<'a> AdminAppState<'a> {
|
||||
pub(crate) async fn create_manual_proxy_node(
|
||||
&self,
|
||||
mutation: &aether_data::repository::proxy_nodes::ProxyNodeManualCreateMutation,
|
||||
) -> Result<Option<aether_data::repository::proxy_nodes::StoredProxyNode>, GatewayError> {
|
||||
self.app.create_manual_proxy_node(mutation).await
|
||||
}
|
||||
|
||||
pub(crate) async fn update_manual_proxy_node(
|
||||
&self,
|
||||
mutation: &aether_data::repository::proxy_nodes::ProxyNodeManualUpdateMutation,
|
||||
) -> Result<Option<aether_data::repository::proxy_nodes::StoredProxyNode>, GatewayError> {
|
||||
self.app.update_manual_proxy_node(mutation).await
|
||||
}
|
||||
|
||||
pub(crate) async fn register_proxy_node(
|
||||
&self,
|
||||
mutation: &aether_data::repository::proxy_nodes::ProxyNodeRegistrationMutation,
|
||||
@@ -94,6 +108,13 @@ impl<'a> AdminAppState<'a> {
|
||||
self.app.unregister_proxy_node(node_id).await
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_proxy_node(
|
||||
&self,
|
||||
node_id: &str,
|
||||
) -> Result<Option<aether_data::repository::proxy_nodes::StoredProxyNode>, GatewayError> {
|
||||
self.app.delete_proxy_node(node_id).await
|
||||
}
|
||||
|
||||
pub(crate) async fn update_proxy_node_remote_config(
|
||||
&self,
|
||||
mutation: &aether_data::repository::proxy_nodes::ProxyNodeRemoteConfigMutation,
|
||||
|
||||
Reference in New Issue
Block a user