Download webui/src/features/vercel/useVercelSyncState.js from a3216/ds2api: direct link, hf CLI and curl.
- Browser
- Download file 7.53 kB
-
https://huggingface.co/spaces/a3216/ds2api/resolve/ce5a019598af8a3a8fa70d3880098df3e850ecd7/webui/src/features/vercel/useVercelSyncState.js
- Command line
-
hf download hf://spaces/a3216/ds2api@ce5a019598af8a3a8fa70d3880098df3e850ecd7/webui/src/features/vercel/useVercelSyncState.js
-
curl -L -o useVercelSyncState.js https://huggingface.co/spaces/a3216/ds2api/resolve/ce5a019598af8a3a8fa70d3880098df3e850ecd7/webui/src/features/vercel/useVercelSyncState.js
7.53 kB
| import { useCallback, useEffect, useState } from 'react' | |
| const MAX_POLL_FAILURES = 3 | |
| function pollDelayMs(attempt) { | |
| if (attempt <= 0) return 15000 | |
| if (attempt === 1) return 30000 | |
| return 60000 | |
| } | |
| export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false }) { | |
| const [vercelToken, setVercelToken] = useState('') | |
| const [projectId, setProjectId] = useState('') | |
| const [teamId, setTeamId] = useState('') | |
| const [saveCredentials, setSaveCredentials] = useState(true) | |
| const [configSource, setConfigSource] = useState('memory') | |
| const [localConfig, setLocalConfig] = useState(null) | |
| const [localConfigName, setLocalConfigName] = useState('') | |
| const [localConfigError, setLocalConfigError] = useState('') | |
| const [loading, setLoading] = useState(false) | |
| const [result, setResult] = useState(null) | |
| const [preconfig, setPreconfig] = useState(null) | |
| const [syncStatus, setSyncStatus] = useState(null) | |
| const [pollPaused, setPollPaused] = useState(false) | |
| const [pollFailures, setPollFailures] = useState(0) | |
| const [nextRetryAt, setNextRetryAt] = useState(null) | |
| const fetchSyncStatus = useCallback(async ({ manual = false } = {}) => { | |
| try { | |
| const useOverride = configSource === 'local' && localConfig | |
| const res = await apiFetch('/admin/vercel/status', useOverride ? { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ config_override: localConfig }), | |
| } : undefined) | |
| if (!res.ok) { | |
| throw new Error(`status ${res.status}`) | |
| } | |
| const data = await res.json() | |
| setSyncStatus(data) | |
| setPollFailures(0) | |
| setPollPaused(false) | |
| setNextRetryAt(null) | |
| } catch (e) { | |
| setPollFailures((prev) => { | |
| const next = prev + 1 | |
| if (isVercel) { | |
| if (next >= MAX_POLL_FAILURES) { | |
| setPollPaused(true) | |
| setNextRetryAt(null) | |
| } else { | |
| setNextRetryAt(Date.now() + pollDelayMs(next)) | |
| } | |
| } | |
| return next | |
| }) | |
| if (manual) { | |
| onMessage('error', t('vercel.networkError')) | |
| } | |
| // eslint-disable-next-line no-console | |
| console.error('Failed to fetch sync status:', e) | |
| } | |
| }, [apiFetch, configSource, isVercel, localConfig, onMessage, t]) | |
| useEffect(() => { | |
| const loadPreconfig = async () => { | |
| try { | |
| const res = await apiFetch('/admin/vercel/config') | |
| if (res.ok) { | |
| const data = await res.json() | |
| setPreconfig(data) | |
| if (data.project_id) setProjectId(data.project_id) | |
| if (data.team_id) setTeamId(data.team_id) | |
| } | |
| } catch (e) { | |
| // eslint-disable-next-line no-console | |
| console.error('Failed to load preconfig:', e) | |
| } | |
| } | |
| loadPreconfig() | |
| fetchSyncStatus() | |
| }, [apiFetch, fetchSyncStatus]) | |
| useEffect(() => { | |
| fetchSyncStatus() | |
| }, [configSource, fetchSyncStatus, localConfig]) | |
| useEffect(() => { | |
| if (!isVercel) { | |
| const interval = setInterval(() => { | |
| fetchSyncStatus() | |
| }, 15000) | |
| return () => clearInterval(interval) | |
| } | |
| if (pollPaused) { | |
| return undefined | |
| } | |
| const delay = nextRetryAt && nextRetryAt > Date.now() ? nextRetryAt - Date.now() : pollDelayMs(pollFailures) | |
| const timer = setTimeout(() => { | |
| fetchSyncStatus() | |
| }, Math.max(1000, delay)) | |
| return () => clearTimeout(timer) | |
| }, [fetchSyncStatus, isVercel, nextRetryAt, pollFailures, pollPaused]) | |
| const handleManualRefresh = useCallback(() => { | |
| setPollPaused(false) | |
| setPollFailures(0) | |
| setNextRetryAt(null) | |
| fetchSyncStatus({ manual: true }) | |
| }, [fetchSyncStatus]) | |
| const handleConfigSourceChange = useCallback((source) => { | |
| setConfigSource(source) | |
| setLocalConfigError('') | |
| }, []) | |
| const handleLocalConfigSelect = useCallback(async (file) => { | |
| if (!file) { | |
| setLocalConfig(null) | |
| setLocalConfigName('') | |
| setLocalConfigError('') | |
| return | |
| } | |
| try { | |
| const text = await file.text() | |
| const parsed = JSON.parse(text) | |
| setLocalConfig(parsed) | |
| setLocalConfigName(file.name || 'config.json') | |
| setLocalConfigError('') | |
| } catch (_e) { | |
| setLocalConfig(null) | |
| setLocalConfigName('') | |
| setLocalConfigError(t('vercel.localConfigInvalid')) | |
| } | |
| }, [t]) | |
| const clearLocalConfig = useCallback(() => { | |
| setLocalConfig(null) | |
| setLocalConfigName('') | |
| setLocalConfigError('') | |
| }, []) | |
| const handleSync = useCallback(async () => { | |
| const tokenToUse = preconfig?.has_token && !vercelToken ? '__USE_PRECONFIG__' : vercelToken | |
| if (!tokenToUse && !preconfig?.has_token) { | |
| onMessage('error', t('vercel.tokenRequired')) | |
| return | |
| } | |
| if (!projectId) { | |
| onMessage('error', t('vercel.projectRequired')) | |
| return | |
| } | |
| if (configSource === 'local' && !localConfig) { | |
| onMessage('error', localConfigError || t('vercel.localConfigMissing')) | |
| return | |
| } | |
| setLoading(true) | |
| setResult(null) | |
| try { | |
| const configOverride = configSource === 'local' ? localConfig : undefined | |
| const res = await apiFetch('/admin/vercel/sync', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| vercel_token: tokenToUse, | |
| project_id: projectId, | |
| team_id: teamId || undefined, | |
| save_credentials: saveCredentials, | |
| config_override: configOverride, | |
| }), | |
| }) | |
| const data = await res.json() | |
| if (res.ok) { | |
| setResult({ ...data, success: true }) | |
| onMessage('success', data.message) | |
| fetchSyncStatus() | |
| } else { | |
| setResult({ ...data, success: false }) | |
| onMessage('error', data.detail || t('vercel.syncFailed')) | |
| } | |
| } catch (_e) { | |
| onMessage('error', t('vercel.networkError')) | |
| } finally { | |
| setLoading(false) | |
| } | |
| }, [apiFetch, configSource, fetchSyncStatus, localConfig, localConfigError, onMessage, preconfig?.has_token, projectId, saveCredentials, t, teamId, vercelToken]) | |
| return { | |
| vercelToken, | |
| setVercelToken, | |
| projectId, | |
| setProjectId, | |
| teamId, | |
| setTeamId, | |
| saveCredentials, | |
| setSaveCredentials, | |
| configSource, | |
| handleConfigSourceChange, | |
| localConfig, | |
| localConfigName, | |
| localConfigError, | |
| handleLocalConfigSelect, | |
| clearLocalConfig, | |
| loading, | |
| result, | |
| preconfig, | |
| syncStatus, | |
| pollPaused, | |
| pollFailures, | |
| handleManualRefresh, | |
| handleSync, | |
| } | |
| } | |