mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-10 11:17:41 +08:00
159 lines
5.3 KiB
JavaScript
159 lines
5.3 KiB
JavaScript
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 [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 res = await apiFetch('/admin/vercel/status')
|
|
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, isVercel, 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(() => {
|
|
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 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
|
|
}
|
|
|
|
setLoading(true)
|
|
setResult(null)
|
|
try {
|
|
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,
|
|
}),
|
|
})
|
|
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, fetchSyncStatus, onMessage, preconfig?.has_token, projectId, saveCredentials, t, teamId, vercelToken])
|
|
|
|
return {
|
|
vercelToken,
|
|
setVercelToken,
|
|
projectId,
|
|
setProjectId,
|
|
teamId,
|
|
setTeamId,
|
|
saveCredentials,
|
|
setSaveCredentials,
|
|
loading,
|
|
result,
|
|
preconfig,
|
|
syncStatus,
|
|
pollPaused,
|
|
pollFailures,
|
|
handleManualRefresh,
|
|
handleSync,
|
|
}
|
|
}
|