BrowserUse_and_ComputerUse_.../api/routes/captcha.py

105 lines
4.2 KiB
Python

import asyncio
from fastapi import APIRouter, Depends, HTTPException, Query
from api.contracts.captcha_schemas import (
CaptchaAbortRequest,
CaptchaActionResponse,
CaptchaExtendRequest,
CaptchaNotifyRequest,
CaptchaSolvedRequest,
CaptchaStatusResponse,
)
from api.mappers.captcha_mapper import CaptchaMapper
from api.routes.dependencies import get_task_service
from api.services.protocols import TaskServiceProtocol
router = APIRouter(prefix="/api/browser", tags=["browser-captcha"])
@router.post("/tasks/{task_id}/captcha/notify", response_model=CaptchaActionResponse, status_code=202)
async def captcha_notify(
task_id: str,
payload: CaptchaNotifyRequest,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaActionResponse:
rec = await service.notify_captcha(
task_id=task_id,
kind=payload.captcha_kind,
reason=payload.reason,
view_url=payload.browser_view_url,
timeout_seconds=payload.timeout_seconds,
)
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_action(rec, message="captcha awaiting solution")
@router.get("/tasks/{task_id}/captcha", response_model=CaptchaStatusResponse)
async def captcha_status(
task_id: str,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaStatusResponse:
rec = await service.get_task(task_id)
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_status(rec)
@router.get("/tasks/{task_id}/captcha/wait", response_model=CaptchaStatusResponse)
async def captcha_wait(
task_id: str,
timeout: float = Query(default=30.0, ge=0.1, le=600.0, description="long-poll cap, seconds"),
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaStatusResponse:
rec = await service.wait_captcha(task_id=task_id, timeout=timeout)
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_status(rec)
@router.post("/tasks/{task_id}/captcha/solved", response_model=CaptchaActionResponse)
async def captcha_solved(
task_id: str,
payload: CaptchaSolvedRequest | None = None,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaActionResponse:
rec = await service.mark_captcha_solved(task_id=task_id, detector=(payload.detector if payload else None))
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_action(rec, message="captcha marked as solved")
@router.post("/tasks/{task_id}/captcha/timeout-prompt", response_model=CaptchaActionResponse)
async def captcha_timeout_prompt(
task_id: str,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaActionResponse:
rec = await service.prompt_captcha_timeout(task_id=task_id)
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_action(rec, message="user must choose: extend or abort")
@router.post("/tasks/{task_id}/captcha/extend", response_model=CaptchaActionResponse)
async def captcha_extend(
task_id: str,
payload: CaptchaExtendRequest,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaActionResponse:
rec = await service.extend_captcha(task_id=task_id, extra_seconds=payload.extra_seconds)
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_action(rec, message=f"captcha extended by {payload.extra_seconds}s")
@router.post("/tasks/{task_id}/captcha/abort", response_model=CaptchaActionResponse)
async def captcha_abort(
task_id: str,
payload: CaptchaAbortRequest | None = None,
service: TaskServiceProtocol = Depends(get_task_service),
) -> CaptchaActionResponse:
rec = await service.abort_captcha(task_id=task_id, reason=(payload.reason if payload else None))
if rec is None:
raise HTTPException(status_code=404, detail="Task not found")
return CaptchaMapper.to_action(rec, message="captcha aborted by user")