feat: introduce a new Config page for managing site settings and proxy configurations, and add a Team Registration page with backend API support.

This commit is contained in:
2026-02-01 07:33:35 +08:00
parent 0c5beddebe
commit 28bdc9d509
3 changed files with 295 additions and 52 deletions

View File

@@ -23,11 +23,16 @@ export default function Config() {
const { config, isConnected, refreshConfig } = useConfig()
const [siteName, setSiteName] = useState('')
const [defaultProxy, setDefaultProxy] = useState('')
const [teamRegProxy, setTeamRegProxy] = useState('')
const [saving, setSaving] = useState(false)
const [savingProxy, setSavingProxy] = useState(false)
const [savingTeamRegProxy, setSavingTeamRegProxy] = useState(false)
const [testingProxy, setTestingProxy] = useState(false)
const [testingTeamRegProxy, setTestingTeamRegProxy] = useState(false)
const [proxyStatus, setProxyStatus] = useState<'unknown' | 'success' | 'error'>('unknown')
const [teamRegProxyStatus, setTeamRegProxyStatus] = useState<'unknown' | 'success' | 'error'>('unknown')
const [proxyOriginIP, setProxyOriginIP] = useState('')
const [teamRegProxyIP, setTeamRegProxyIP] = useState('')
const { toasts, toast, removeToast } = useToast()
// 加载站点名称和代理配置
@@ -39,6 +44,7 @@ export default function Config() {
if (data.code === 0 && data.data) {
setSiteName(data.data.site_name || 'Codex Pool')
setDefaultProxy(data.data.default_proxy || '')
setTeamRegProxy(data.data.team_reg_proxy || '')
// 恢复代理测试状态
const testStatus = data.data.proxy_test_status
if (testStatus === 'success' || testStatus === 'error') {
@@ -135,6 +141,61 @@ export default function Config() {
}
}
// 保存注册代理地址
const handleSaveTeamRegProxy = async () => {
setSavingTeamRegProxy(true)
try {
const res = await fetch('/api/config', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ team_reg_proxy: teamRegProxy }),
})
const data = await res.json()
if (data.code === 0) {
toast.success('注册代理地址已保存')
refreshConfig()
} else {
toast.error(data.message || '保存失败')
}
} catch {
toast.error('网络错误')
} finally {
setSavingTeamRegProxy(false)
}
}
// 测试注册代理连接
const handleTestTeamRegProxy = async () => {
if (!teamRegProxy.trim()) {
toast.error('请先输入注册代理地址')
return
}
setTestingTeamRegProxy(true)
setTeamRegProxyStatus('unknown')
setTeamRegProxyIP('')
try {
const res = await fetch('/api/proxy/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ proxy_url: teamRegProxy }),
})
const data = await res.json()
if (data.code === 0 && data.data?.connected) {
setTeamRegProxyStatus('success')
setTeamRegProxyIP(data.data.origin_ip || '')
toast.success(`注册代理连接成功${data.data.origin_ip ? `, 出口IP: ${data.data.origin_ip}` : ''}`)
} else {
setTeamRegProxyStatus('error')
toast.error(data.message || '注册代理连接失败')
}
} catch (e) {
setTeamRegProxyStatus('error')
toast.error(e instanceof Error ? e.message : '网络错误')
} finally {
setTestingTeamRegProxy(false)
}
}
const configItems = [
{
to: '/config/s2a',
@@ -307,6 +368,72 @@ export default function Config() {
)}
</p>
</div>
{/* 注册代理地址配置 */}
<div className="pt-4 border-t border-slate-200 dark:border-slate-700">
<div className="flex items-center gap-2 mb-2">
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300">
</label>
{/* 代理状态徽章 */}
{teamRegProxyStatus === 'success' && (
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400">
<Wifi className="h-3 w-3" />
</span>
)}
{teamRegProxyStatus === 'error' && (
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400">
<WifiOff className="h-3 w-3" />
</span>
)}
{teamRegProxyStatus === 'unknown' && teamRegProxy && (
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-400">
<HelpCircle className="h-3 w-3" />
</span>
)}
</div>
<div className="flex gap-3">
<Input
value={teamRegProxy}
onChange={(e) => {
setTeamRegProxy(e.target.value)
setTeamRegProxyStatus('unknown')
}}
placeholder="http://user:pass@host:port"
className="flex-1"
/>
<Button
size="sm"
onClick={handleSaveTeamRegProxy}
disabled={savingTeamRegProxy}
icon={savingTeamRegProxy ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
className="shrink-0"
>
{savingTeamRegProxy ? '保存中...' : '保存'}
</Button>
<Button
size="sm"
variant="outline"
onClick={handleTestTeamRegProxy}
disabled={testingTeamRegProxy || !teamRegProxy.trim()}
icon={testingTeamRegProxy ? <Loader2 className="h-4 w-4 animate-spin" /> : <Wifi className="h-4 w-4" />}
className="shrink-0"
>
{testingTeamRegProxy ? '测试中...' : '测试连接'}
</Button>
</div>
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400">
Team 使使
{teamRegProxyIP && (
<span className="ml-2 text-green-600 dark:text-green-400">
IP: {teamRegProxyIP}
</span>
)}
</p>
</div>
</CardContent>
</Card>

View File

@@ -39,6 +39,7 @@ export default function TeamReg() {
// 配置表单
const [count, setCount] = useState(5)
const [concurrency, setConcurrency] = useState(2)
const [useProxy, setUseProxy] = useState(false)
const [proxy, setProxy] = useState('')
const [autoImport, setAutoImport] = useState(true)
@@ -46,6 +47,27 @@ export default function TeamReg() {
const logsContainerRef = useRef<HTMLDivElement>(null)
const [autoScroll, setAutoScroll] = useState(true)
// 加载保存的代理配置(从配置页面读取)
const loadProxyConfig = useCallback(async () => {
try {
const res = await fetch('/api/config')
if (res.ok) {
const data = await res.json()
if (data.code === 0 && data.data?.team_reg_proxy) {
setProxy(data.data.team_reg_proxy)
setUseProxy(true)
}
}
} catch (e) {
console.error('加载代理配置失败:', e)
}
}, [])
// 初始化时加载代理配置
useEffect(() => {
loadProxyConfig()
}, [loadProxyConfig])
// 获取状态
const fetchStatus = useCallback(async () => {
setLoading(true)
@@ -90,7 +112,7 @@ export default function TeamReg() {
body: JSON.stringify({
count,
concurrency,
proxy,
proxy: useProxy ? proxy : '',
auto_import: autoImport,
}),
})
@@ -289,15 +311,42 @@ export default function TeamReg() {
hint="同时进行的注册任务数 (1-10)"
disabled={isRunning}
/>
<Input
label="代理地址"
type="text"
value={proxy}
onChange={(e) => setProxy(e.target.value)}
placeholder="留空使用默认代理"
hint="HTTP 代理地址,如 http://127.0.0.1:7890"
disabled={isRunning}
/>
{/* 代理配置区域 */}
<div className="p-3 rounded-lg bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 space-y-3">
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={useProxy}
onChange={(e) => setUseProxy(e.target.checked)}
disabled={isRunning}
className="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"
/>
<div>
<p className="font-medium text-slate-900 dark:text-slate-100">使</p>
<p className="text-xs text-slate-500">使</p>
</div>
</label>
{useProxy && proxy && (
<div className="ml-7 p-2 rounded bg-slate-100 dark:bg-slate-700">
<p className="text-sm text-slate-700 dark:text-slate-300 font-mono break-all">
{proxy}
</p>
<p className="text-xs text-slate-500 mt-1">
<a href="/config" className="text-blue-500 hover:underline"></a>
</p>
</div>
)}
{useProxy && !proxy && (
<p className="ml-7 text-xs text-orange-500">
<a href="/config" className="text-blue-500 hover:underline"></a>
</p>
)}
</div>
{/* 自动导入开关 */}
<div className="p-3 rounded-lg bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700">
<label className="flex items-center gap-3 cursor-pointer">
<input