Show Git ingestion job status
This commit is contained in:
+55
-8
@@ -1,8 +1,11 @@
|
|||||||
"""WebUI FastAPI application."""
|
"""WebUI FastAPI application."""
|
||||||
|
import asyncio
|
||||||
import html
|
import html
|
||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from fastapi import FastAPI, File, Form, Request, UploadFile
|
from fastapi import FastAPI, File, Form, Request, UploadFile
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||||
@@ -23,6 +26,8 @@ templates.env.globals["escapeHtml"] = lambda value: html.escape(str(value or "")
|
|||||||
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static")
|
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static")
|
||||||
|
|
||||||
_client: Optional[DocsAPIClient] = None
|
_client: Optional[DocsAPIClient] = None
|
||||||
|
_sync_jobs: Dict[str, Dict[str, Any]] = {}
|
||||||
|
_sync_tasks: set[asyncio.Task] = set()
|
||||||
|
|
||||||
|
|
||||||
def get_client() -> DocsAPIClient:
|
def get_client() -> DocsAPIClient:
|
||||||
@@ -35,6 +40,25 @@ def get_client() -> DocsAPIClient:
|
|||||||
return _client
|
return _client
|
||||||
|
|
||||||
|
|
||||||
|
def utc_now() -> str:
|
||||||
|
return datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
async def run_sync_job(job_id: str, override: bool) -> None:
|
||||||
|
job = _sync_jobs[job_id]
|
||||||
|
job["status"] = "running"
|
||||||
|
job["started_at"] = utc_now()
|
||||||
|
try:
|
||||||
|
result = await get_client().post("/sources/sync", json={"override": override})
|
||||||
|
job["result"] = result
|
||||||
|
job["status"] = "succeeded" if result.get("success") else "failed"
|
||||||
|
except Exception as exc:
|
||||||
|
job["status"] = "failed"
|
||||||
|
job["error"] = str(exc)
|
||||||
|
finally:
|
||||||
|
job["finished_at"] = utc_now()
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
@app.on_event("shutdown")
|
||||||
async def shutdown() -> None:
|
async def shutdown() -> None:
|
||||||
if _client is not None:
|
if _client is not None:
|
||||||
@@ -300,10 +324,33 @@ async def add_source(
|
|||||||
|
|
||||||
@app.post("/sources/sync")
|
@app.post("/sources/sync")
|
||||||
async def sync_sources(override: bool = Form(False)):
|
async def sync_sources(override: bool = Form(False)):
|
||||||
client = get_client()
|
job_id = uuid.uuid4().hex
|
||||||
try:
|
_sync_jobs[job_id] = {
|
||||||
result = await client.post("/sources/sync", json={"override": override})
|
"id": job_id,
|
||||||
body = f"<h1>Git Sync Complete</h1><pre>{html.escape(str(result))}</pre><a href='/sources'>Back</a>"
|
"status": "queued",
|
||||||
except Exception as e:
|
"created_at": utc_now(),
|
||||||
body = f"<h1>Git Sync Failed</h1><pre>{html.escape(str(e))}</pre><a href='/sources'>Back</a>"
|
"started_at": None,
|
||||||
return page("Git Sync", body)
|
"finished_at": None,
|
||||||
|
"result": None,
|
||||||
|
"error": None,
|
||||||
|
}
|
||||||
|
task = asyncio.create_task(run_sync_job(job_id, override))
|
||||||
|
_sync_tasks.add(task)
|
||||||
|
task.add_done_callback(_sync_tasks.discard)
|
||||||
|
return RedirectResponse(url=f"/sources/jobs/{job_id}", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/sources/jobs/{job_id}")
|
||||||
|
async def sync_job_page(request: Request, job_id: str):
|
||||||
|
job = _sync_jobs.get(job_id)
|
||||||
|
if job is None:
|
||||||
|
return page("Git Sync Not Found", "<h1>Git Sync Not Found</h1><a href='/sources'>Back</a>")
|
||||||
|
return templates.TemplateResponse("sync_job.html", {"request": request, "job": job})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/sources/jobs/{job_id}/status")
|
||||||
|
async def sync_job_status(job_id: str):
|
||||||
|
job = _sync_jobs.get(job_id)
|
||||||
|
if job is None:
|
||||||
|
return JSONResponse(status_code=404, content={"error": "Sync job not found"})
|
||||||
|
return job
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Git Sync Status - Context7 Docs{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Git Sync Status</h2>
|
||||||
|
|
||||||
|
<div class="status-message">
|
||||||
|
Status: <strong id="job-status">{{ job.status }}</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if job.status in ['queued', 'running'] %}
|
||||||
|
<p>Cloning, reading, embedding, and indexing documents. Large repositories can take several minutes.</p>
|
||||||
|
<p>This page updates automatically. You can leave it open or return later using the same URL.</p>
|
||||||
|
{% elif job.error %}
|
||||||
|
<h3>Sync Failed</h3>
|
||||||
|
<pre>{{ job.error }}</pre>
|
||||||
|
{% elif job.result %}
|
||||||
|
<h3>Summary</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Sources attempted: {{ job.result.total_sources | default(0) }}</li>
|
||||||
|
<li>Successful: {{ job.result.successful | default(0) }}</li>
|
||||||
|
<li>Failed: {{ job.result.failed | default(0) }}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% for result in job.result.results | default([]) %}
|
||||||
|
<div class="source-card">
|
||||||
|
<strong>{{ result.library_id | default('unknown') }}</strong><br>
|
||||||
|
Status: {{ 'succeeded' if result.success else 'failed' }}<br>
|
||||||
|
{% if result.success %}
|
||||||
|
Files discovered: {{ result.files_discovered | default(0) }}<br>
|
||||||
|
Chunks created: {{ result.chunks_created | default(0) }}<br>
|
||||||
|
Vectors added: {{ result.vectors_added | default(0) }}<br>
|
||||||
|
<a href="/libraries/{{ result.library_id }}/docs">View indexed documents</a>
|
||||||
|
{% else %}
|
||||||
|
Error: {{ result.error | default('Unknown ingestion error') }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p><a href="/sources">Back to sources</a> | <a href="/libraries">View libraries</a> | <a href="/search">Test search</a></p>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% if job.status in ['queued', 'running'] %}
|
||||||
|
<script>
|
||||||
|
setTimeout(() => window.location.reload(), 3000);
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user