| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- /*
- Copyright (C) 2025 QuantumNous
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
- For commercial licensing, please contact support@quantumnous.com
- */
- import React, { useEffect, useState } from 'react';
- import { useTranslation } from 'react-i18next';
- import {
- Modal,
- Button,
- Space,
- Typography,
- Input,
- Banner,
- } from '@douyinfe/semi-ui';
- import { API, copy, showError, showSuccess } from '../../../../helpers';
- const { Text } = Typography;
- const CodexOAuthModal = ({ visible, onCancel, onSuccess }) => {
- const { t } = useTranslation();
- const [loading, setLoading] = useState(false);
- const [authorizeUrl, setAuthorizeUrl] = useState('');
- const [input, setInput] = useState('');
- const startOAuth = async () => {
- setLoading(true);
- try {
- const res = await API.post(
- '/api/channel/codex/oauth/start',
- {},
- { skipErrorHandler: true },
- );
- if (!res?.data?.success) {
- console.error('Codex OAuth start failed:', res?.data?.message);
- throw new Error(t('启动授权失败'));
- }
- const url = res?.data?.data?.authorize_url || '';
- if (!url) {
- console.error(
- 'Codex OAuth start response missing authorize_url:',
- res?.data,
- );
- throw new Error(t('响应缺少授权链接'));
- }
- setAuthorizeUrl(url);
- window.open(url, '_blank', 'noopener,noreferrer');
- showSuccess(t('已打开授权页面'));
- } catch (error) {
- showError(error?.message || t('启动授权失败'));
- } finally {
- setLoading(false);
- }
- };
- const completeOAuth = async () => {
- if (!input || !input.trim()) {
- showError(t('请先粘贴回调 URL'));
- return;
- }
- setLoading(true);
- try {
- const res = await API.post(
- '/api/channel/codex/oauth/complete',
- { input },
- { skipErrorHandler: true },
- );
- if (!res?.data?.success) {
- console.error('Codex OAuth complete failed:', res?.data?.message);
- throw new Error(t('授权失败'));
- }
- const key = res?.data?.data?.key || '';
- if (!key) {
- console.error('Codex OAuth complete response missing key:', res?.data);
- throw new Error(t('响应缺少凭据'));
- }
- onSuccess && onSuccess(key);
- showSuccess(t('已生成授权凭据'));
- onCancel && onCancel();
- } catch (error) {
- showError(error?.message || t('授权失败'));
- } finally {
- setLoading(false);
- }
- };
- useEffect(() => {
- if (!visible) return;
- setAuthorizeUrl('');
- setInput('');
- }, [visible]);
- return (
- <Modal
- title={t('Codex 授权')}
- visible={visible}
- onCancel={onCancel}
- maskClosable={false}
- closeOnEsc
- width={720}
- footer={
- <Space>
- <Button theme='borderless' onClick={onCancel} disabled={loading}>
- {t('取消')}
- </Button>
- <Button
- theme='solid'
- type='primary'
- onClick={completeOAuth}
- loading={loading}
- >
- {t('生成并填入')}
- </Button>
- </Space>
- }
- >
- <Space vertical spacing='tight' style={{ width: '100%' }}>
- <Banner
- type='info'
- description={t(
- '1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。',
- )}
- />
- <Space wrap>
- <Button type='primary' onClick={startOAuth} loading={loading}>
- {t('打开授权页面')}
- </Button>
- <Button
- theme='outline'
- disabled={!authorizeUrl || loading}
- onClick={() => copy(authorizeUrl)}
- >
- {t('复制授权链接')}
- </Button>
- </Space>
- <Input
- value={input}
- onChange={(value) => setInput(value)}
- placeholder={t('请粘贴完整回调 URL(包含 code 与 state)')}
- showClear
- />
- <Text type='tertiary' size='small'>
- {t(
- '说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。',
- )}
- </Text>
- </Space>
- </Modal>
- );
- };
- export default CodexOAuthModal;
|