mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 11:43:57 +08:00
feat(xai): add Grok account management UI with four-locale strings
Add XaiOAuthSection (device-code login, account list, default selection) backed by the generalized useManagedAuth hook, wire it into the Auth Center and both Claude provider forms, and expose model fetching for signed-in accounts. - Accounts requiring re-auth stay visible in the account selector as disabled items with an expired badge instead of silently vanishing. - Auth status refetches periodically so a revoked refresh token surfaces without reloading the panel. - All strings ship in zh/en/ja/zh-TW; a locale coverage test pins every required key in all four locales.
This commit is contained in:
@@ -107,6 +107,7 @@ import {
|
||||
useHermesFormState,
|
||||
useCopilotAuth,
|
||||
useCodexOauth,
|
||||
useXaiOauth,
|
||||
} from "./hooks";
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
import { useSettingsQuery } from "@/lib/query";
|
||||
@@ -514,10 +515,19 @@ function ProviderFormFull({
|
||||
);
|
||||
|
||||
// Copilot OAuth 认证状态(仅 Claude 应用需要)
|
||||
const { isAuthenticated: isCopilotAuthenticated } = useCopilotAuth();
|
||||
const { isAuthenticated: isCopilotAuthenticated, accounts: copilotAccounts } =
|
||||
useCopilotAuth();
|
||||
|
||||
// Codex OAuth 认证状态(ChatGPT Plus/Pro 反代)
|
||||
const { isAuthenticated: isCodexOauthAuthenticated } = useCodexOauth();
|
||||
const {
|
||||
isAuthenticated: isCodexOauthAuthenticated,
|
||||
accounts: codexOauthAccounts,
|
||||
} = useCodexOauth();
|
||||
|
||||
const {
|
||||
isAuthenticated: isXaiOauthAuthenticated,
|
||||
accounts: xaiOauthAccounts,
|
||||
} = useXaiOauth();
|
||||
|
||||
// 选中的 GitHub 账号 ID(多账号支持)
|
||||
const [selectedGitHubAccountId, setSelectedGitHubAccountId] = useState<
|
||||
@@ -528,6 +538,9 @@ function ProviderFormFull({
|
||||
const [selectedCodexAccountId, setSelectedCodexAccountId] = useState<
|
||||
string | null
|
||||
>(() => resolveManagedAccountId(initialData?.meta, "codex_oauth"));
|
||||
const [selectedXaiAccountId, setSelectedXaiAccountId] = useState<
|
||||
string | null
|
||||
>(() => resolveManagedAccountId(initialData?.meta, "xai_oauth"));
|
||||
const [codexFastMode, setCodexFastMode] = useState<boolean>(
|
||||
() => initialData?.meta?.codexFastMode ?? false,
|
||||
);
|
||||
@@ -1146,6 +1159,9 @@ function ProviderFormFull({
|
||||
const isCodexOauthProvider =
|
||||
templatePreset?.providerType === "codex_oauth" ||
|
||||
initialData?.meta?.providerType === "codex_oauth";
|
||||
const isXaiOauthProvider =
|
||||
templatePreset?.providerType === "xai_oauth" ||
|
||||
initialData?.meta?.providerType === "xai_oauth";
|
||||
if (isCopilotProvider && !isCopilotAuthenticated) {
|
||||
toast.error(
|
||||
t("copilot.loginRequired", {
|
||||
@@ -1162,6 +1178,56 @@ function ProviderFormFull({
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (isXaiOauthProvider && !isXaiOauthAuthenticated) {
|
||||
toast.error(
|
||||
t("xaiOauth.loginRequired", {
|
||||
defaultValue: "请先登录 xAI 账号",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedAccountIsUsable = (
|
||||
accountId: string | null,
|
||||
accounts: Array<{ id: string; requires_reauth: boolean }>,
|
||||
) =>
|
||||
accountId === null ||
|
||||
accounts.some(
|
||||
(account) => account.id === accountId && !account.requires_reauth,
|
||||
);
|
||||
if (
|
||||
isCopilotProvider &&
|
||||
!selectedAccountIsUsable(selectedGitHubAccountId, copilotAccounts)
|
||||
) {
|
||||
toast.error(
|
||||
t("managedAuth.selectedAccountUnavailable", {
|
||||
defaultValue: "已绑定账号不存在,请重新选择账号",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
isCodexOauthProvider &&
|
||||
!selectedAccountIsUsable(selectedCodexAccountId, codexOauthAccounts)
|
||||
) {
|
||||
toast.error(
|
||||
t("managedAuth.selectedAccountUnavailable", {
|
||||
defaultValue: "已绑定账号不存在,请重新选择账号",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
isXaiOauthProvider &&
|
||||
!selectedAccountIsUsable(selectedXaiAccountId, xaiOauthAccounts)
|
||||
) {
|
||||
toast.error(
|
||||
t("managedAuth.selectedAccountNeedsReauth", {
|
||||
defaultValue: "已绑定 xAI 账号不存在或需要重新登录",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// OMO Other Fields JSON:B 类(格式错了保存下去数据就坏了)
|
||||
if (
|
||||
@@ -1198,14 +1264,19 @@ function ProviderFormFull({
|
||||
// cloud_provider(如 Bedrock)通过模板变量处理认证,跳过通用校验
|
||||
if (category !== "official" && category !== "cloud_provider") {
|
||||
if (appId === "claude") {
|
||||
if (!isCodexOauthProvider && !baseUrl.trim()) {
|
||||
if (!isCodexOauthProvider && !isXaiOauthProvider && !baseUrl.trim()) {
|
||||
issues.push(
|
||||
t("providerForm.endpointRequired", {
|
||||
defaultValue: "非官方供应商请填写 API 端点",
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (!isCopilotProvider && !isCodexOauthProvider && !apiKey.trim()) {
|
||||
if (
|
||||
!isCopilotProvider &&
|
||||
!isCodexOauthProvider &&
|
||||
!isXaiOauthProvider &&
|
||||
!apiKey.trim()
|
||||
) {
|
||||
issues.push(
|
||||
t("providerForm.apiKeyRequired", {
|
||||
defaultValue: "非官方供应商请填写 API Key",
|
||||
@@ -1278,6 +1349,9 @@ function ProviderFormFull({
|
||||
const isCodexOauthProvider =
|
||||
templatePreset?.providerType === "codex_oauth" ||
|
||||
initialData?.meta?.providerType === "codex_oauth";
|
||||
const isXaiOauthProvider =
|
||||
templatePreset?.providerType === "xai_oauth" ||
|
||||
initialData?.meta?.providerType === "xai_oauth";
|
||||
|
||||
let settingsConfig: string;
|
||||
|
||||
@@ -1483,7 +1557,13 @@ function ProviderFormFull({
|
||||
authProvider: "codex_oauth",
|
||||
accountId: selectedCodexAccountId ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
: isXaiOauthProvider
|
||||
? {
|
||||
source: "managed_account",
|
||||
authProvider: "xai_oauth",
|
||||
accountId: selectedXaiAccountId ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
// GitHub Copilot 多账号:保存关联的账号 ID
|
||||
githubAccountId:
|
||||
isCopilotProvider && selectedGitHubAccountId
|
||||
@@ -1519,7 +1599,9 @@ function ProviderFormFull({
|
||||
: undefined,
|
||||
apiFormat:
|
||||
appId === "claude" && category !== "official"
|
||||
? localApiFormat
|
||||
? isXaiOauthProvider
|
||||
? "openai_responses"
|
||||
: localApiFormat
|
||||
: appId === "codex" && category !== "official"
|
||||
? localCodexApiFormat
|
||||
: undefined,
|
||||
@@ -1552,7 +1634,10 @@ function ProviderFormFull({
|
||||
? Number(localCodexMaxOutputTokens)
|
||||
: undefined,
|
||||
isFullUrl:
|
||||
supportsFullUrl && category !== "official" && localIsFullUrl
|
||||
supportsFullUrl &&
|
||||
category !== "official" &&
|
||||
!isXaiOauthProvider &&
|
||||
localIsFullUrl
|
||||
? true
|
||||
: undefined,
|
||||
};
|
||||
@@ -2109,13 +2194,19 @@ function ProviderFormFull({
|
||||
templatePreset?.providerType === "codex_oauth" ||
|
||||
initialData?.meta?.providerType === "codex_oauth"
|
||||
}
|
||||
isXaiOauthPreset={
|
||||
templatePreset?.providerType === "xai_oauth" ||
|
||||
initialData?.meta?.providerType === "xai_oauth"
|
||||
}
|
||||
usesOAuth={
|
||||
templatePreset?.requiresOAuth === true ||
|
||||
templatePreset?.providerType === "github_copilot" ||
|
||||
initialData?.meta?.providerType === "github_copilot" ||
|
||||
baseUrl.includes("githubcopilot.com") ||
|
||||
templatePreset?.providerType === "codex_oauth" ||
|
||||
initialData?.meta?.providerType === "codex_oauth"
|
||||
initialData?.meta?.providerType === "codex_oauth" ||
|
||||
templatePreset?.providerType === "xai_oauth" ||
|
||||
initialData?.meta?.providerType === "xai_oauth"
|
||||
}
|
||||
isCopilotAuthenticated={isCopilotAuthenticated}
|
||||
selectedGitHubAccountId={selectedGitHubAccountId}
|
||||
@@ -2125,6 +2216,9 @@ function ProviderFormFull({
|
||||
onCodexAccountSelect={setSelectedCodexAccountId}
|
||||
codexFastMode={codexFastMode}
|
||||
onCodexFastModeChange={setCodexFastMode}
|
||||
isXaiOauthAuthenticated={isXaiOauthAuthenticated}
|
||||
selectedXaiAccountId={selectedXaiAccountId}
|
||||
onXaiAccountSelect={setSelectedXaiAccountId}
|
||||
templateValueEntries={templateValueEntries}
|
||||
templateValues={templateValues}
|
||||
templatePresetName={templatePreset?.name || ""}
|
||||
|
||||
Reference in New Issue
Block a user