MVP frontend+backend
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import { useState, useRef, useCallback } from 'react'
|
||||
import * as api from '@/services/api'
|
||||
import { isUnauthorized } from '@/services/errors'
|
||||
|
||||
export type ScraperStatus = 'idle' | 'launching' | 'awaiting_scan' | 'authenticating' | 'fetching' | 'done' | 'error' | 'cancelled'
|
||||
|
||||
interface PollResponse {
|
||||
status: ScraperStatus
|
||||
qr_data?: string
|
||||
result?: Record<string, unknown>
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface ResolvedIds {
|
||||
workspace_id: string
|
||||
personal_data_uuid: string
|
||||
child_id: string
|
||||
}
|
||||
|
||||
const POLL_INTERVAL_MS = 1000
|
||||
|
||||
interface UseScraperFetchResult {
|
||||
sessionId: string | null
|
||||
pollData: PollResponse | null
|
||||
resolvedIds: ResolvedIds | null
|
||||
error: string | null
|
||||
isUnauthorized: boolean
|
||||
start: (childId?: string) => Promise<void>
|
||||
cancel: () => Promise<void>
|
||||
isRunning: boolean
|
||||
isTerminal: boolean
|
||||
}
|
||||
|
||||
export function useScraperFetch(): UseScraperFetchResult {
|
||||
const [sessionId, setSessionId] = useState<string | null>(null)
|
||||
const [pollData, setPollData] = useState<PollResponse | null>(null)
|
||||
const [resolvedIds, setResolvedIds] = useState<ResolvedIds | null>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [unauthorizedError, setUnauthorizedError] = useState(false)
|
||||
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
const stopPolling = useCallback(() => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current)
|
||||
intervalRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
const isTerminalStatus = (s: ScraperStatus) =>
|
||||
s === 'done' || s === 'error' || s === 'cancelled'
|
||||
|
||||
const startPolling = useCallback((sid: string) => {
|
||||
stopPolling()
|
||||
intervalRef.current = setInterval(async () => {
|
||||
try {
|
||||
const data = await api.pollScraperSession(sid)
|
||||
setPollData(data as PollResponse)
|
||||
if (isTerminalStatus((data as PollResponse).status)) {
|
||||
stopPolling()
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Poll error')
|
||||
stopPolling()
|
||||
}
|
||||
}, POLL_INTERVAL_MS)
|
||||
}, [stopPolling])
|
||||
|
||||
const start = useCallback(
|
||||
async (childId?: string) => {
|
||||
if (sessionId && pollData && !isTerminalStatus(pollData.status)) {
|
||||
stopPolling()
|
||||
try {
|
||||
await api.cancelScraperSession(sessionId)
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
setError(null)
|
||||
setUnauthorizedError(false)
|
||||
setPollData(null)
|
||||
setSessionId(null)
|
||||
setResolvedIds(null)
|
||||
|
||||
try {
|
||||
const payload = childId ? { child_id: childId } : {}
|
||||
const resp = await api.startScraperSession(payload)
|
||||
setSessionId(resp.session_id)
|
||||
setResolvedIds({
|
||||
workspace_id: resp.workspace_id,
|
||||
personal_data_uuid: resp.personal_data_uuid,
|
||||
child_id: resp.child_id,
|
||||
})
|
||||
setPollData({ status: 'launching' })
|
||||
startPolling(resp.session_id)
|
||||
} catch (err) {
|
||||
if (isUnauthorized(err)) {
|
||||
setUnauthorizedError(true)
|
||||
setError('Not authenticated')
|
||||
} else {
|
||||
setError(err instanceof Error ? err.message : 'Start error')
|
||||
}
|
||||
}
|
||||
},
|
||||
[sessionId, pollData, stopPolling, startPolling],
|
||||
)
|
||||
|
||||
const cancel = useCallback(async () => {
|
||||
stopPolling()
|
||||
if (sessionId) {
|
||||
try {
|
||||
await api.cancelScraperSession(sessionId)
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
setPollData({ status: 'cancelled' })
|
||||
setSessionId(null)
|
||||
}, [sessionId, stopPolling])
|
||||
|
||||
const isRunning = sessionId !== null && pollData !== null && !isTerminalStatus(pollData.status)
|
||||
const isTerminal = pollData !== null && isTerminalStatus(pollData.status)
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
pollData,
|
||||
resolvedIds,
|
||||
error,
|
||||
isUnauthorized: unauthorizedError,
|
||||
start,
|
||||
cancel,
|
||||
isRunning,
|
||||
isTerminal,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user