Merge remote-tracking branch 'origin/pr/487'

# Conflicts:
#	crates/aether-data/src/lifecycle/bootstrap/postgres.rs
#	crates/aether-data/src/lifecycle/migrate/tests.rs
#	crates/aether-data/src/repository/oauth_providers/postgres.rs
#	crates/aether-data/src/repository/oauth_providers/sqlite.rs
#	frontend/src/views/admin/OAuthSettings.vue
This commit is contained in:
fawney19
2026-05-19 02:27:39 +08:00
27 changed files with 293 additions and 204 deletions

View File

@@ -28,6 +28,8 @@ pub(crate) struct AdminOAuthProviderUpsertRequest {
#[serde(default)]
pub(super) extra_config: Option<serde_json::Value>,
#[serde(default)]
pub(super) icon_url: Option<String>,
#[serde(default)]
pub(super) is_enabled: bool,
#[serde(default)]
pub(super) force: bool,
@@ -70,6 +72,7 @@ pub(super) fn build_admin_oauth_provider_payload(
"frontend_callback_url": provider.frontend_callback_url,
"attribute_mapping": provider.attribute_mapping,
"extra_config": provider.extra_config,
"icon_url": provider.icon_url,
"is_enabled": provider.is_enabled,
})
}
@@ -364,6 +367,10 @@ pub(super) fn build_admin_oauth_upsert_record(
frontend_callback_url: frontend_callback_url.to_string(),
attribute_mapping: payload.attribute_mapping,
extra_config: payload.extra_config,
icon_url: payload.icon_url.and_then(|value| {
let value = value.trim().to_string();
(!value.is_empty()).then_some(value)
}),
is_enabled: payload.is_enabled,
})
}

View File

@@ -16,7 +16,7 @@ use axum::{
use serde_json::json;
use std::time::Duration;
const ADMIN_OAUTH_TEST_TIMEOUT_SECS: u64 = 5;
const ADMIN_OAUTH_TEST_TIMEOUT_SECS: u64 = 10;
const LINUXDO_AUTHORIZATION_URL: &str = "https://connect.linux.do/oauth2/authorize";
const LINUXDO_TOKEN_URL: &str = "https://connect.linux.do/oauth2/token";
@@ -54,10 +54,7 @@ async fn admin_oauth_endpoint_reachable(client: &reqwest::Client, url: &str) ->
.send()
.await
{
Ok(response) => {
let status = response.status();
status != reqwest::StatusCode::NOT_FOUND && status.as_u16() < 500
}
Ok(response) => response.status().as_u16() < 500,
Err(_) => false,
}
}
@@ -120,10 +117,16 @@ async fn build_admin_oauth_test_payload(
}));
};
let client = reqwest::Client::builder()
let proxy_snapshot = state.app().resolve_system_proxy_snapshot().await;
let mut client_builder = reqwest::Client::builder()
.timeout(Duration::from_secs(ADMIN_OAUTH_TEST_TIMEOUT_SECS))
.redirect(reqwest::redirect::Policy::limited(3))
.build();
.redirect(reqwest::redirect::Policy::limited(3));
if let Some(proxy_url) = proxy_snapshot.as_ref().and_then(|p| p.url.as_deref()) {
if let Ok(proxy) = reqwest::Proxy::all(proxy_url) {
client_builder = client_builder.proxy(proxy);
}
}
let client = client_builder.build();
let Ok(client) = client else {
return Ok(json!({
"authorization_url_reachable": false,

View File

@@ -1885,6 +1885,7 @@ impl<'a> AdminAppState<'a> {
oauth_provider.extra_config,
"extra_config",
)),
icon_url: None,
is_enabled: oauth_provider.is_enabled,
};
invalid!(record.validate().map_err(|err| err.to_string()));

View File

@@ -16,6 +16,8 @@ const LINUXDO_USERINFO_URL: &str = "https://connect.linux.do/api/user";
pub(crate) struct IdentityOAuthProviderSummary {
pub(crate) provider_type: String,
pub(crate) display_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) icon_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
@@ -77,6 +79,7 @@ pub(crate) async fn list_enabled_identity_oauth_providers(
.map(|provider| IdentityOAuthProviderSummary {
provider_type: provider.provider_type,
display_name: provider.display_name,
icon_url: provider.icon_url,
})
.collect::<Vec<_>>();
providers.sort_by(|left, right| left.provider_type.cmp(&right.provider_type));

View File

@@ -436,6 +436,7 @@ pub(super) fn sample_oauth_provider_config(provider_type: &str) -> StoredOAuthPr
Some(vec!["openid".to_string()]),
Some(json!({"email": "email"})),
Some(json!({"team": true})),
None,
true,
)
}