import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react' import type { AppConfig } from '../types' import { defaultConfig } from '../types' import { saveConfig } from '../utils/storage' import { S2AClient } from '../api/s2a' interface ConfigContextValue { config: AppConfig updateConfig: (updates: Partial) => void updateS2AConfig: (updates: Partial) => void updatePoolingConfig: (updates: Partial) => void updateCheckConfig: (updates: Partial) => void updateEmailConfig: (updates: Partial) => void isConnected: boolean testConnection: () => Promise s2aClient: S2AClient | null refreshConfig: () => Promise siteName: string } const ConfigContext = createContext(null) export function ConfigProvider({ children }: { children: ReactNode }) { const [config, setConfig] = useState(defaultConfig) const [isConnected, setIsConnected] = useState(false) const [s2aClient, setS2aClient] = useState(null) const [siteName, setSiteName] = useState('Codex Pool') // Load config from server on mount const refreshConfig = useCallback(async () => { try { const res = await fetch('/api/config') const data = await res.json() if (data.code === 0 && data.data) { const serverConfig = data.data setConfig(prev => ({ ...prev, s2a: { ...prev.s2a, apiBase: serverConfig.s2a_api_base || '', adminKey: serverConfig.s2a_admin_key || '', }, pooling: { ...prev.pooling, concurrency: serverConfig.concurrency || 2, priority: serverConfig.priority || 0, groupIds: serverConfig.group_ids || [], }, })) // 更新站点名称 if (serverConfig.site_name) { setSiteName(serverConfig.site_name) } } } catch (error) { console.error('Failed to load config from server:', error) } }, []) useEffect(() => { refreshConfig() }, [refreshConfig]) // Update S2A client when config changes and auto-test connection useEffect(() => { if (config.s2a.apiBase && config.s2a.adminKey) { const client = new S2AClient({ baseUrl: config.s2a.apiBase, apiKey: config.s2a.adminKey, }) setS2aClient(client) // 自动测试连接 fetch('/api/s2a/test') .then(res => res.json()) .then(data => { setIsConnected(data.code === 0) }) .catch(() => { setIsConnected(false) }) } else { setS2aClient(null) setIsConnected(false) } }, [config.s2a.apiBase, config.s2a.adminKey]) const updateConfig = useCallback((updates: Partial) => { setConfig((prev) => { const newConfig = { ...prev, ...updates } saveConfig(newConfig) return newConfig }) }, []) const updateS2AConfig = useCallback((updates: Partial) => { setConfig((prev) => { const newConfig = { ...prev, s2a: { ...prev.s2a, ...updates }, } saveConfig(newConfig) return newConfig }) }, []) const updatePoolingConfig = useCallback((updates: Partial) => { setConfig((prev) => { const newConfig = { ...prev, pooling: { ...prev.pooling, ...updates }, } saveConfig(newConfig) return newConfig }) }, []) const updateCheckConfig = useCallback((updates: Partial) => { setConfig((prev) => { const newConfig = { ...prev, check: { ...prev.check, ...updates }, } saveConfig(newConfig) return newConfig }) }, []) const updateEmailConfig = useCallback((updates: Partial) => { setConfig((prev) => { const newConfig = { ...prev, email: { ...prev.email, ...updates }, } saveConfig(newConfig) return newConfig }) }, []) const testConnection = useCallback(async (): Promise => { try { // 使用后端代理 API 来测试 S2A 连接(避免 CORS 问题) const res = await fetch('/api/s2a/test') const data = await res.json() const connected = data.code === 0 setIsConnected(connected) return connected } catch { setIsConnected(false) return false } }, []) return ( {children} ) } export function useConfigContext(): ConfigContextValue { const context = useContext(ConfigContext) if (!context) { throw new Error('useConfigContext must be used within a ConfigProvider') } return context }