peijun1's picture
Deploy AI Studio Proxy API to Hugging Face Spaces
a5784e9
Raw
History Blame Contribute Delete
4.85 kB
/**
* Proxy Settings Component
* Configure browser proxy settings with connectivity testing
*/
import { useState, useEffect } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { Wifi, RefreshCw, AlertCircle, CheckCircle, Loader2 } from 'lucide-react';
import { useI18n } from '@/contexts';
import { fetchProxyConfig, updateProxyConfig, testProxyConnectivity } from '@/api';
import type { ProxyConfig, ProxyTestResult } from '@/api';
import styles from './SettingsPanel.module.css';
export function ProxySettings() {
const { t } = useI18n();
const queryClient = useQueryClient();
const [localConfig, setLocalConfig] = useState<ProxyConfig>({ enabled: false, address: 'http://127.0.0.1:7890' });
const [testResult, setTestResult] = useState<ProxyTestResult | null>(null);
const [hasChanges, setHasChanges] = useState(false);
// Fetch current config
const { data: config, isLoading } = useQuery({
queryKey: ['proxyConfig'],
queryFn: fetchProxyConfig,
});
// Update local state when config loads
useEffect(() => {
if (config) {
setLocalConfig(config);
setHasChanges(false);
}
}, [config]);
// Update config mutation
const updateMutation = useMutation({
mutationFn: updateProxyConfig,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['proxyConfig'] });
setHasChanges(false);
},
});
// Test connectivity mutation
const testMutation = useMutation({
mutationFn: (address: string) => testProxyConnectivity(address),
onSuccess: (result) => setTestResult(result),
});
const handleEnabledChange = (enabled: boolean) => {
setLocalConfig((prev) => ({ ...prev, enabled }));
setHasChanges(true);
};
const handleAddressChange = (address: string) => {
setLocalConfig((prev) => ({ ...prev, address }));
setHasChanges(true);
setTestResult(null);
};
const handleSave = () => {
updateMutation.mutate(localConfig);
};
const handleTest = () => {
if (localConfig.address.trim()) {
testMutation.mutate(localConfig.address);
}
};
if (isLoading) {
return (
<div className={styles.loading}>
<Loader2 size={16} className={styles.spinning} />
<span>{t.common.loading}</span>
</div>
);
}
return (
<div className={styles.proxySettings}>
{/* Enable Toggle */}
<div className={styles.toggle}>
<div className={styles.toggleLabel}>
<span className={styles.label}>{t.settingsPage.enableBrowserProxy}</span>
<span className={styles.description}>{t.settingsPage.enableBrowserProxyDesc}</span>
</div>
<button
className={`${styles.switch} ${localConfig.enabled ? styles.active : ''}`}
onClick={() => handleEnabledChange(!localConfig.enabled)}
role="switch"
aria-checked={localConfig.enabled}
>
<span className={styles.switchThumb} aria-hidden="true" />
</button>
</div>
{/* Proxy Address Input */}
<div className={styles.formGroup}>
<label className={styles.label}>{t.settingsPage.proxyAddress}</label>
<div className={styles.inputGroup}>
<input
type="text"
className={styles.input}
value={localConfig.address}
onChange={(e) => handleAddressChange(e.target.value)}
placeholder="http://127.0.0.1:7890"
aria-label={t.settingsPage.proxyAddress}
/>
</div>
</div>
{/* Action Buttons */}
<div className={styles.buttonGroup}>
<button
className={styles.secondaryButton}
onClick={handleTest}
disabled={!localConfig.address.trim() || testMutation.isPending}
>
{testMutation.isPending ? (
<Loader2 size={14} className={styles.spinning} />
) : (
<Wifi size={14} />
)}
{t.settingsPage.testConnection}
</button>
<button
className={styles.primaryButton}
onClick={handleSave}
disabled={!hasChanges || updateMutation.isPending}
>
{updateMutation.isPending ? (
<Loader2 size={14} className={styles.spinning} />
) : (
<RefreshCw size={14} />
)}
{t.common.save}
</button>
</div>
{/* Test Result */}
{testResult && (
<div
className={`${styles.alertBox} ${testResult.success ? styles.success : styles.error}`}
>
{testResult.success ? <CheckCircle size={16} /> : <AlertCircle size={16} />}
<span>
{testResult.message}
{testResult.latency_ms && ` (${testResult.latency_ms}ms)`}
</span>
</div>
)}
</div>
);
}