mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-18 18:50:49 +08:00
Merge pull request #28 from notdp/main
feat(oauth): add provider icons to oauth login cards
This commit is contained in:
@@ -5,6 +5,17 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
}
|
||||
|
||||
.cardTitleIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.pageTitle {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -9,8 +9,13 @@ import { HeaderInputList } from '@/components/ui/HeaderInputList';
|
||||
import { ModelInputList, modelsToEntries, entriesToModels } from '@/components/ui/ModelInputList';
|
||||
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
|
||||
import { IconCheck, IconX } from '@/components/ui/icons';
|
||||
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
|
||||
import { useAuthStore, useConfigStore, useNotificationStore, useThemeStore } from '@/stores';
|
||||
import { ampcodeApi, modelsApi, providersApi, usageApi } from '@/services/api';
|
||||
import iconGemini from '@/assets/icons/gemini.svg';
|
||||
import iconOpenaiLight from '@/assets/icons/openai-light.svg';
|
||||
import iconOpenaiDark from '@/assets/icons/openai-dark.svg';
|
||||
import iconClaude from '@/assets/icons/claude.svg';
|
||||
import iconAmp from '@/assets/icons/amp.svg';
|
||||
import type {
|
||||
GeminiKeyConfig,
|
||||
ProviderKeyConfig,
|
||||
@@ -181,6 +186,7 @@ const buildAmpcodeFormState = (ampcode?: AmpcodeConfig | null): AmpcodeFormState
|
||||
export function AiProvidersPage() {
|
||||
const { t } = useTranslation();
|
||||
const { showNotification } = useNotificationStore();
|
||||
const { theme } = useThemeStore();
|
||||
const connectionStatus = useAuthStore((state) => state.connectionStatus);
|
||||
|
||||
const config = useConfigStore((state) => state.config);
|
||||
@@ -1158,7 +1164,12 @@ export function AiProvidersPage() {
|
||||
{error && <div className="error-box">{error}</div>}
|
||||
|
||||
<Card
|
||||
title={t('ai_providers.gemini_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={iconGemini} alt="" className={styles.cardTitleIcon} />
|
||||
{t('ai_providers.gemini_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -1264,7 +1275,12 @@ export function AiProvidersPage() {
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title={t('ai_providers.codex_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={theme === 'dark' ? iconOpenaiDark : iconOpenaiLight} alt="" className={styles.cardTitleIcon} />
|
||||
{t('ai_providers.codex_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -1375,7 +1391,12 @@ export function AiProvidersPage() {
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title={t('ai_providers.claude_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={iconClaude} alt="" className={styles.cardTitleIcon} />
|
||||
{t('ai_providers.claude_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -1502,7 +1523,12 @@ export function AiProvidersPage() {
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title={t('ai_providers.ampcode_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={iconAmp} alt="" className={styles.cardTitleIcon} />
|
||||
{t('ai_providers.ampcode_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -1575,7 +1601,12 @@ export function AiProvidersPage() {
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title={t('ai_providers.openai_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={theme === 'dark' ? iconOpenaiDark : iconOpenaiLight} alt="" className={styles.cardTitleIcon} />
|
||||
{t('ai_providers.openai_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
}
|
||||
|
||||
.cardTitleIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.pageTitle {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -3,9 +3,16 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { useNotificationStore } from '@/stores';
|
||||
import { useNotificationStore, useThemeStore } from '@/stores';
|
||||
import { oauthApi, type OAuthProvider, type IFlowCookieAuthResponse } from '@/services/api/oauth';
|
||||
import styles from './OAuthPage.module.scss';
|
||||
import iconOpenaiLight from '@/assets/icons/openai-light.svg';
|
||||
import iconOpenaiDark from '@/assets/icons/openai-dark.svg';
|
||||
import iconClaude from '@/assets/icons/claude.svg';
|
||||
import iconAntigravity from '@/assets/icons/antigravity.svg';
|
||||
import iconGemini from '@/assets/icons/gemini.svg';
|
||||
import iconQwen from '@/assets/icons/qwen.svg';
|
||||
import iconIflow from '@/assets/icons/iflow.svg';
|
||||
|
||||
interface ProviderState {
|
||||
url?: string;
|
||||
@@ -29,20 +36,25 @@ interface IFlowCookieState {
|
||||
errorType?: 'error' | 'warning';
|
||||
}
|
||||
|
||||
const PROVIDERS: { id: OAuthProvider; titleKey: string; hintKey: string; urlLabelKey: string }[] = [
|
||||
{ id: 'codex', titleKey: 'auth_login.codex_oauth_title', hintKey: 'auth_login.codex_oauth_hint', urlLabelKey: 'auth_login.codex_oauth_url_label' },
|
||||
{ id: 'anthropic', titleKey: 'auth_login.anthropic_oauth_title', hintKey: 'auth_login.anthropic_oauth_hint', urlLabelKey: 'auth_login.anthropic_oauth_url_label' },
|
||||
{ id: 'antigravity', titleKey: 'auth_login.antigravity_oauth_title', hintKey: 'auth_login.antigravity_oauth_hint', urlLabelKey: 'auth_login.antigravity_oauth_url_label' },
|
||||
{ id: 'gemini-cli', titleKey: 'auth_login.gemini_cli_oauth_title', hintKey: 'auth_login.gemini_cli_oauth_hint', urlLabelKey: 'auth_login.gemini_cli_oauth_url_label' },
|
||||
{ id: 'qwen', titleKey: 'auth_login.qwen_oauth_title', hintKey: 'auth_login.qwen_oauth_hint', urlLabelKey: 'auth_login.qwen_oauth_url_label' },
|
||||
{ id: 'iflow', titleKey: 'auth_login.iflow_oauth_title', hintKey: 'auth_login.iflow_oauth_hint', urlLabelKey: 'auth_login.iflow_oauth_url_label' }
|
||||
const PROVIDERS: { id: OAuthProvider; titleKey: string; hintKey: string; urlLabelKey: string; icon: string | { light: string; dark: string } }[] = [
|
||||
{ id: 'codex', titleKey: 'auth_login.codex_oauth_title', hintKey: 'auth_login.codex_oauth_hint', urlLabelKey: 'auth_login.codex_oauth_url_label', icon: { light: iconOpenaiLight, dark: iconOpenaiDark } },
|
||||
{ id: 'anthropic', titleKey: 'auth_login.anthropic_oauth_title', hintKey: 'auth_login.anthropic_oauth_hint', urlLabelKey: 'auth_login.anthropic_oauth_url_label', icon: iconClaude },
|
||||
{ id: 'antigravity', titleKey: 'auth_login.antigravity_oauth_title', hintKey: 'auth_login.antigravity_oauth_hint', urlLabelKey: 'auth_login.antigravity_oauth_url_label', icon: iconAntigravity },
|
||||
{ id: 'gemini-cli', titleKey: 'auth_login.gemini_cli_oauth_title', hintKey: 'auth_login.gemini_cli_oauth_hint', urlLabelKey: 'auth_login.gemini_cli_oauth_url_label', icon: iconGemini },
|
||||
{ id: 'qwen', titleKey: 'auth_login.qwen_oauth_title', hintKey: 'auth_login.qwen_oauth_hint', urlLabelKey: 'auth_login.qwen_oauth_url_label', icon: iconQwen },
|
||||
{ id: 'iflow', titleKey: 'auth_login.iflow_oauth_title', hintKey: 'auth_login.iflow_oauth_hint', urlLabelKey: 'auth_login.iflow_oauth_url_label', icon: iconIflow }
|
||||
];
|
||||
|
||||
const CALLBACK_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli', 'iflow'];
|
||||
|
||||
const getIcon = (icon: string | { light: string; dark: string }, theme: 'light' | 'dark') => {
|
||||
return typeof icon === 'string' ? icon : icon[theme];
|
||||
};
|
||||
|
||||
export function OAuthPage() {
|
||||
const { t } = useTranslation();
|
||||
const { showNotification } = useNotificationStore();
|
||||
const { theme } = useThemeStore();
|
||||
const [states, setStates] = useState<Record<OAuthProvider, ProviderState>>({} as Record<OAuthProvider, ProviderState>);
|
||||
const [iflowCookie, setIflowCookie] = useState<IFlowCookieState>({ cookie: '', loading: false });
|
||||
const timers = useRef<Record<string, number>>({});
|
||||
@@ -215,7 +227,12 @@ export function OAuthPage() {
|
||||
return (
|
||||
<div key={provider.id}>
|
||||
<Card
|
||||
title={t(provider.titleKey)}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={getIcon(provider.icon, theme)} alt="" className={styles.cardTitleIcon} />
|
||||
{t(provider.titleKey)}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button onClick={() => startAuth(provider.id)} loading={state.polling}>
|
||||
{t('common.login')}
|
||||
@@ -309,7 +326,12 @@ export function OAuthPage() {
|
||||
|
||||
{/* iFlow Cookie 登录 */}
|
||||
<Card
|
||||
title={t('auth_login.iflow_cookie_title')}
|
||||
title={
|
||||
<span className={styles.cardTitle}>
|
||||
<img src={iconIflow} alt="" className={styles.cardTitleIcon} />
|
||||
{t('auth_login.iflow_cookie_title')}
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Button onClick={submitIflowCookie} loading={iflowCookie.loading}>
|
||||
{t('auth_login.iflow_cookie_button')}
|
||||
|
||||
Reference in New Issue
Block a user