feat: add compare endpoint to webui, fix duplicate Done. line
All checks were successful
Deploy / deploy (push) Successful in 13s

- Add /compare POST endpoint that runs the two-instance diff
- Add "Compare Instances" button to the dashboard
- Fix double "Done." output: the summary line was logged both as a
  regular log event and as the done event message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 23:50:20 +01:00
parent 89797e7d42
commit f20d41355b

View File

@@ -258,9 +258,10 @@ async def run_sync_job(job_id: str, command: str) -> None:
summary_line = "" summary_line = ""
async for raw in proc.stdout: async for raw in proc.stdout:
text = raw.decode(errors="replace").rstrip() text = raw.decode(errors="replace").rstrip()
_jobs[job_id]["output"].append({"type": "log", "message": text})
if text.startswith("Done."): if text.startswith("Done."):
summary_line = text summary_line = text
else:
_jobs[job_id]["output"].append({"type": "log", "message": text})
await proc.wait() await proc.wait()
success = proc.returncode == 0 success = proc.returncode == 0
_jobs[job_id]["output"].append({ _jobs[job_id]["output"].append({
@@ -514,6 +515,7 @@ async def dashboard():
<button class="btn btn-primary" onclick="doSync('/pull','Pulling from Outline')">Get from Outline</button> <button class="btn btn-primary" onclick="doSync('/pull','Pulling from Outline')">Get from Outline</button>
<button class="btn btn-success" onclick="doSync('/push','Sending to Outline')"{push_dis}>{push_label}</button> <button class="btn btn-success" onclick="doSync('/push','Sending to Outline')"{push_dis}>{push_label}</button>
<a href="/changes" class="btn btn-secondary">Preview Changes</a> <a href="/changes" class="btn btn-secondary">Preview Changes</a>
<button class="btn btn-secondary" onclick="doSync('/compare','Comparing instances')">Compare Instances</button>
</div> </div>
<div id="output"></div> <div id="output"></div>
</div> </div>
@@ -552,6 +554,21 @@ async def start_pull():
return {"job_id": job_id, "stream_url": f"/stream/{job_id}"} return {"job_id": job_id, "stream_url": f"/stream/{job_id}"}
@app.post("/compare")
async def start_compare():
if _active_job is not None:
raise HTTPException(status_code=409, detail="A sync job is already running")
settings = _load_settings()
local = settings.get("local", {})
if not local.get("url") or not local.get("token"):
return JSONResponse(
status_code=400,
content={"detail": "Local instance not configured — set LOCAL_OUTLINE_URL and LOCAL_OUTLINE_TOKEN env vars"},
)
job_id = _new_job("compare")
return {"job_id": job_id, "stream_url": f"/stream/{job_id}"}
@app.post("/push") @app.post("/push")
async def start_push(): async def start_push():
if _active_job is not None: if _active_job is not None: