feat: processing flow enhancement + responsive UI
Phase 2 - Processing flow:
- Multi-task monitoring: store supports concurrent task tracking
- Task retry: POST /api/tasks/{id}/retry re-runs failed tasks
- Dashboard multi-task cards with progress, error details, retry/dismiss
- Log panel expanded from 10 to 50 lines with "view all" link
Phase 3 - UI/UX:
- Mobile sidebar drawer (< 768px) with hamburger menu
- Layout responsive styles (768px, 480px breakpoints)
- Tasks/Logs pages responsive (stat cards, filters, columns)
- File views responsive (header wrap, button sizing)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -121,7 +121,34 @@ async def retry_task(
|
||||
"""Retry a failed task by re-invoking its processing endpoint.
|
||||
|
||||
Only tasks with status ``failed`` may be retried.
|
||||
For in-memory tasks with metadata, the original endpoint and request body
|
||||
are used to faithfully reproduce the original call. For historical DB-only
|
||||
tasks, the endpoint is looked up from ``_RETRY_ROUTE_MAP`` by task name.
|
||||
"""
|
||||
tm = request.state.task_manager
|
||||
|
||||
# --- Strategy 1: in-memory task with metadata ---
|
||||
new_task = tm.retry_task(task_id)
|
||||
if new_task is not None:
|
||||
meta = new_task.metadata or {}
|
||||
endpoint = meta.get("endpoint")
|
||||
body = meta.get("body", {})
|
||||
if endpoint:
|
||||
base_url = f"http://{request.url.hostname}:{request.url.port}"
|
||||
url = f"{base_url}{endpoint}"
|
||||
auth_header = request.headers.get("authorization")
|
||||
headers: dict[str, str] = {}
|
||||
if auth_header:
|
||||
headers["authorization"] = auth_header
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.post(url, json=body, headers=headers)
|
||||
return {"task_id": new_task.id, "status": "retried", "original_response": resp.json()}
|
||||
|
||||
# Metadata present but no endpoint — fall through to DB strategy
|
||||
# (the new task was already created; caller can track it)
|
||||
return {"task_id": new_task.id, "status": "retried"}
|
||||
|
||||
# --- Strategy 2: DB-only historical task (no in-memory record) ---
|
||||
loop = asyncio.get_event_loop()
|
||||
task = await loop.run_in_executor(
|
||||
None, lambda: db_schema.query_task_by_id(task_id),
|
||||
@@ -142,18 +169,18 @@ async def retry_task(
|
||||
detail=f"未知的任务类型: {task_name}",
|
||||
)
|
||||
|
||||
# Build the internal URL to the processing endpoint.
|
||||
# Create a new in-memory task to track the retry.
|
||||
new_task = tm.create_task(task_name)
|
||||
|
||||
base_url = f"http://{request.url.hostname}:{request.url.port}"
|
||||
url = f"{base_url}{endpoint}"
|
||||
|
||||
# Forward the Authorization header so the processing endpoint can
|
||||
# authenticate the request.
|
||||
auth_header = request.headers.get("authorization")
|
||||
headers: dict[str, str] = {}
|
||||
headers = {}
|
||||
if auth_header:
|
||||
headers["authorization"] = auth_header
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.post(url, headers=headers)
|
||||
|
||||
return resp.json()
|
||||
return {"task_id": new_task.id, "status": "retried", "original_response": resp.json()}
|
||||
|
||||
Reference in New Issue
Block a user