mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-03 02:51:17 +08:00
feat: display subscription quota for Codex OAuth provider cards
Codex OAuth (ChatGPT Plus/Pro) providers previously fell through to the
default UsageFooter branch and showed no quota at all, while Copilot and
official Codex providers already had a wham/usage-backed quota footer.
This wires up the same five-hour / seven-day tier badges for codex_oauth
provider cards by reusing the existing query_codex_quota function and
SubscriptionQuotaFooter rendering, parameterized to keep both the CLI
credential path ("codex") and the cc-switch managed OAuth path
("codex_oauth") working from a single source of truth.
- Parameterize services::subscription::query_codex_quota with tool_label
and expired_message; promote SubscriptionQuota constructors to
pub(crate). The CLI path keeps its existing "codex" label and the
"re-login with Codex CLI" message; the new path passes "codex_oauth"
and a cc-switch-specific re-login hint.
- Add a new get_codex_oauth_quota Tauri command in commands/codex_oauth.rs
that resolves the ChatGPT account (explicit binding > default account
> not_found), pulls a valid access_token from CodexOAuthManager
(auto-refresh handled), and delegates to query_codex_quota.
- Extract SubscriptionQuotaFooter's render body into a pure
SubscriptionQuotaView component (props: quota / loading / refetch /
appIdForExpiredHint / inline). The existing SubscriptionQuotaFooter
becomes a thin wrapper with identical props and behavior, so
CopilotQuotaFooter and the official Claude/Codex/Gemini paths are
untouched. This avoids duplicating ~280 lines of five-state rendering.
- Add CodexOauthQuotaFooter, a 38-line wrapper that calls the new
useCodexOauthQuota hook and forwards to SubscriptionQuotaView.
- ProviderCard inserts an isCodexOauth branch between isCopilot and
isOfficial, keyed off PROVIDER_TYPES.CODEX_OAUTH (newly added to
config/constants.ts to centralize the previously scattered string).
- Frontend hook caches per (codex_oauth, accountId) so multiple cards
bound to the same ChatGPT account share one fetch via react-query
dedup; cards bound to different accounts get independent fetches.
- No new i18n keys: existing subscription.fiveHour / sevenDay / expired /
refresh / queryFailed / expiredHint are reused.
This commit is contained in:
@@ -60,7 +60,7 @@ pub struct SubscriptionQuota {
|
||||
}
|
||||
|
||||
impl SubscriptionQuota {
|
||||
fn not_found(tool: &str) -> Self {
|
||||
pub(crate) fn not_found(tool: &str) -> Self {
|
||||
Self {
|
||||
tool: tool.to_string(),
|
||||
credential_status: CredentialStatus::NotFound,
|
||||
@@ -73,7 +73,7 @@ impl SubscriptionQuota {
|
||||
}
|
||||
}
|
||||
|
||||
fn error(tool: &str, status: CredentialStatus, message: String) -> Self {
|
||||
pub(crate) fn error(tool: &str, status: CredentialStatus, message: String) -> Self {
|
||||
Self {
|
||||
tool: tool.to_string(),
|
||||
credential_status: status,
|
||||
@@ -621,8 +621,17 @@ fn unix_ts_to_iso(ts: i64) -> Option<String> {
|
||||
chrono::DateTime::from_timestamp(ts, 0).map(|dt| dt.to_rfc3339())
|
||||
}
|
||||
|
||||
/// 查询 Codex 官方订阅额度
|
||||
async fn query_codex_quota(access_token: &str, account_id: Option<&str>) -> SubscriptionQuota {
|
||||
/// 查询 Codex / ChatGPT 反代订阅额度
|
||||
///
|
||||
/// 参数化 `tool_label` 和 `expired_message` 让该函数可被两个调用点共用:
|
||||
/// - `"codex"` + "Please re-login with Codex CLI."(CLI 凭据路径)
|
||||
/// - `"codex_oauth"` + "Please re-login via cc-switch."(cc-switch 自管 OAuth 路径)
|
||||
pub(crate) async fn query_codex_quota(
|
||||
access_token: &str,
|
||||
account_id: Option<&str>,
|
||||
tool_label: &str,
|
||||
expired_message: &str,
|
||||
) -> SubscriptionQuota {
|
||||
let client = crate::proxy::http_client::get();
|
||||
|
||||
let mut req = client
|
||||
@@ -639,7 +648,7 @@ async fn query_codex_quota(access_token: &str, account_id: Option<&str>) -> Subs
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return SubscriptionQuota::error(
|
||||
"codex",
|
||||
tool_label,
|
||||
CredentialStatus::Valid,
|
||||
format!("Network error: {e}"),
|
||||
);
|
||||
@@ -650,16 +659,16 @@ async fn query_codex_quota(access_token: &str, account_id: Option<&str>) -> Subs
|
||||
|
||||
if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN {
|
||||
return SubscriptionQuota::error(
|
||||
"codex",
|
||||
tool_label,
|
||||
CredentialStatus::Expired,
|
||||
format!("Authentication failed (HTTP {status}). Please re-login with Codex CLI."),
|
||||
format!("{expired_message} (HTTP {status})"),
|
||||
);
|
||||
}
|
||||
|
||||
if !status.is_success() {
|
||||
let body = resp.text().await.unwrap_or_default();
|
||||
return SubscriptionQuota::error(
|
||||
"codex",
|
||||
tool_label,
|
||||
CredentialStatus::Valid,
|
||||
format!("API error (HTTP {status}): {body}"),
|
||||
);
|
||||
@@ -669,7 +678,7 @@ async fn query_codex_quota(access_token: &str, account_id: Option<&str>) -> Subs
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return SubscriptionQuota::error(
|
||||
"codex",
|
||||
tool_label,
|
||||
CredentialStatus::Valid,
|
||||
format!("Failed to parse API response: {e}"),
|
||||
);
|
||||
@@ -697,7 +706,7 @@ async fn query_codex_quota(access_token: &str, account_id: Option<&str>) -> Subs
|
||||
}
|
||||
|
||||
SubscriptionQuota {
|
||||
tool: "codex".to_string(),
|
||||
tool: tool_label.to_string(),
|
||||
credential_status: CredentialStatus::Valid,
|
||||
credential_message: None,
|
||||
success: true,
|
||||
@@ -1221,7 +1230,13 @@ pub async fn get_subscription_quota(tool: &str) -> Result<SubscriptionQuota, Str
|
||||
CredentialStatus::Expired => {
|
||||
// 即使可能过期也尝试调用 API
|
||||
if let Some(token) = token {
|
||||
let result = query_codex_quota(&token, account_id.as_deref()).await;
|
||||
let result = query_codex_quota(
|
||||
&token,
|
||||
account_id.as_deref(),
|
||||
"codex",
|
||||
"Authentication failed. Please re-login with Codex CLI.",
|
||||
)
|
||||
.await;
|
||||
if result.success {
|
||||
return Ok(result);
|
||||
}
|
||||
@@ -1234,7 +1249,13 @@ pub async fn get_subscription_quota(tool: &str) -> Result<SubscriptionQuota, Str
|
||||
}
|
||||
CredentialStatus::Valid => {
|
||||
let token = token.expect("token must be Some when status is Valid");
|
||||
Ok(query_codex_quota(&token, account_id.as_deref()).await)
|
||||
Ok(query_codex_quota(
|
||||
&token,
|
||||
account_id.as_deref(),
|
||||
"codex",
|
||||
"Authentication failed. Please re-login with Codex CLI.",
|
||||
)
|
||||
.await)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user