mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 10:25:05 +08:00
feat(copilot): add GitHub Enterprise Server support (#2175)
* feat(copilot): add GitHub Enterprise Server support * fix(copilot): address GHES PR review findings (P1 + 2×P2) - P1: Use composite account ID (domain:user_id) for GHES to prevent cross-instance ID collisions; github.com keeps plain numeric ID for backward compatibilit - P2-a: Use get_api_endpoint() for model list URL with automatic fallback to static URL when dynamic endpoint resolution fails - P2-b: Add normalize_github_domain() as backend SSOT for domain normalization (lowercase, strip protocol/path/query, reject userinfo) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ pub struct ManagedAuthAccount {
|
||||
pub avatar_url: Option<String>,
|
||||
pub authenticated_at: i64,
|
||||
pub is_default: bool,
|
||||
pub github_domain: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
@@ -59,6 +60,7 @@ fn map_account(
|
||||
login: account.login,
|
||||
avatar_url: account.avatar_url,
|
||||
authenticated_at: account.authenticated_at,
|
||||
github_domain: account.github_domain,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +81,7 @@ fn map_device_code_response(
|
||||
#[tauri::command(rename_all = "camelCase")]
|
||||
pub async fn auth_start_login(
|
||||
auth_provider: String,
|
||||
github_domain: Option<String>,
|
||||
copilot_state: State<'_, CopilotAuthState>,
|
||||
codex_state: State<'_, CodexOAuthState>,
|
||||
) -> Result<ManagedAuthDeviceCodeResponse, String> {
|
||||
@@ -87,7 +90,7 @@ pub async fn auth_start_login(
|
||||
AUTH_PROVIDER_GITHUB_COPILOT => {
|
||||
let auth_manager = copilot_state.0.read().await;
|
||||
let response = auth_manager
|
||||
.start_device_flow()
|
||||
.start_device_flow(github_domain.as_deref())
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(map_device_code_response(auth_provider, response))
|
||||
@@ -108,6 +111,7 @@ pub async fn auth_start_login(
|
||||
pub async fn auth_poll_for_account(
|
||||
auth_provider: String,
|
||||
device_code: String,
|
||||
github_domain: Option<String>,
|
||||
copilot_state: State<'_, CopilotAuthState>,
|
||||
codex_state: State<'_, CodexOAuthState>,
|
||||
) -> Result<Option<ManagedAuthAccount>, String> {
|
||||
@@ -115,7 +119,10 @@ pub async fn auth_poll_for_account(
|
||||
match auth_provider {
|
||||
AUTH_PROVIDER_GITHUB_COPILOT => {
|
||||
let auth_manager = copilot_state.0.write().await;
|
||||
match auth_manager.poll_for_token(&device_code).await {
|
||||
match auth_manager
|
||||
.poll_for_token(&device_code, github_domain.as_deref())
|
||||
.await
|
||||
{
|
||||
Ok(account) => {
|
||||
let default_account_id = auth_manager.get_status().await.default_account_id;
|
||||
Ok(account.map(|account| {
|
||||
|
||||
@@ -20,11 +20,12 @@ pub struct CopilotAuthState(pub Arc<RwLock<CopilotAuthManager>>);
|
||||
/// 返回设备码和用户码,用于 OAuth 认证
|
||||
#[tauri::command]
|
||||
pub async fn copilot_start_device_flow(
|
||||
github_domain: Option<String>,
|
||||
state: State<'_, CopilotAuthState>,
|
||||
) -> Result<GitHubDeviceCodeResponse, String> {
|
||||
let auth_manager = state.0.read().await;
|
||||
auth_manager
|
||||
.start_device_flow()
|
||||
.start_device_flow(github_domain.as_deref())
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -36,10 +37,14 @@ pub async fn copilot_start_device_flow(
|
||||
#[tauri::command(rename_all = "camelCase")]
|
||||
pub async fn copilot_poll_for_auth(
|
||||
device_code: String,
|
||||
github_domain: Option<String>,
|
||||
state: State<'_, CopilotAuthState>,
|
||||
) -> Result<bool, String> {
|
||||
let auth_manager = state.0.write().await;
|
||||
match auth_manager.poll_for_token(&device_code).await {
|
||||
match auth_manager
|
||||
.poll_for_token(&device_code, github_domain.as_deref())
|
||||
.await
|
||||
{
|
||||
Ok(Some(_account)) => {
|
||||
log::info!("[CopilotAuth] 用户已授权");
|
||||
Ok(true)
|
||||
@@ -61,10 +66,14 @@ pub async fn copilot_poll_for_auth(
|
||||
#[tauri::command(rename_all = "camelCase")]
|
||||
pub async fn copilot_poll_for_account(
|
||||
device_code: String,
|
||||
github_domain: Option<String>,
|
||||
state: State<'_, CopilotAuthState>,
|
||||
) -> Result<Option<GitHubAccount>, String> {
|
||||
let auth_manager = state.0.write().await;
|
||||
match auth_manager.poll_for_token(&device_code).await {
|
||||
match auth_manager
|
||||
.poll_for_token(&device_code, github_domain.as_deref())
|
||||
.await
|
||||
{
|
||||
Ok(account) => Ok(account),
|
||||
Err(crate::proxy::providers::copilot_auth::CopilotAuthError::AuthorizationPending) => {
|
||||
Ok(None)
|
||||
|
||||
Reference in New Issue
Block a user