Add delete button for runs in UI
- Add delete_html section to run detail page with delete button
- Add /ui/runs/{run_id}/discard HTMX endpoint
- Failed runs can always be deleted without restrictions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
66
server.py
66
server.py
@@ -437,6 +437,55 @@ async def discard_run(run_id: str, username: str = Depends(get_required_user)):
|
|||||||
return {"discarded": True, "run_id": run_id}
|
return {"discarded": True, "run_id": run_id}
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/ui/runs/{run_id}/discard", response_class=HTMLResponse)
|
||||||
|
async def ui_discard_run(run_id: str, request: Request):
|
||||||
|
"""HTMX handler: discard a run."""
|
||||||
|
current_user = get_user_from_cookie(request)
|
||||||
|
if not current_user:
|
||||||
|
return '<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Login required</div>'
|
||||||
|
|
||||||
|
run = load_run(run_id)
|
||||||
|
if not run:
|
||||||
|
return '<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Run not found</div>'
|
||||||
|
|
||||||
|
# Check ownership
|
||||||
|
actor_id = f"@{current_user}@{L2_DOMAIN}"
|
||||||
|
if run.username not in (current_user, actor_id):
|
||||||
|
return '<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Access denied</div>'
|
||||||
|
|
||||||
|
# Failed runs can always be deleted
|
||||||
|
if run.status != "failed":
|
||||||
|
# Check if activity exists for this run
|
||||||
|
activity = cache_manager.get_activity(run_id)
|
||||||
|
|
||||||
|
if activity:
|
||||||
|
can_discard, reason = cache_manager.can_discard_activity(run_id)
|
||||||
|
if not can_discard:
|
||||||
|
return f'<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Cannot discard: {reason}</div>'
|
||||||
|
|
||||||
|
success, msg = cache_manager.discard_activity(run_id)
|
||||||
|
if not success:
|
||||||
|
return f'<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Failed to discard: {msg}</div>'
|
||||||
|
else:
|
||||||
|
# Legacy run - check L2 shared status
|
||||||
|
items_to_check = list(run.inputs or [])
|
||||||
|
if run.output_hash:
|
||||||
|
items_to_check.append(run.output_hash)
|
||||||
|
|
||||||
|
for content_hash in items_to_check:
|
||||||
|
if cache_manager.l2_checker.is_shared(content_hash):
|
||||||
|
return f'<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Cannot discard: item {content_hash[:16]}... is published to L2</div>'
|
||||||
|
|
||||||
|
# Remove from Redis
|
||||||
|
redis_client.delete(f"{RUNS_KEY_PREFIX}{run_id}")
|
||||||
|
|
||||||
|
return '''
|
||||||
|
<div class="bg-green-900/50 border border-green-700 text-green-300 px-4 py-3 rounded-lg mb-4">
|
||||||
|
Run deleted. <a href="/runs" class="underline">Back to runs</a>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
@app.get("/run/{run_id}")
|
@app.get("/run/{run_id}")
|
||||||
async def run_detail(run_id: str, request: Request):
|
async def run_detail(run_id: str, request: Request):
|
||||||
"""Run detail. HTML for browsers, JSON for APIs."""
|
"""Run detail. HTML for browsers, JSON for APIs."""
|
||||||
@@ -590,6 +639,22 @@ async def run_detail(run_id: str, request: Request):
|
|||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# Delete section
|
||||||
|
delete_html = f'''
|
||||||
|
<div class="border-t border-dark-500 pt-6 mt-6">
|
||||||
|
<h2 class="text-lg font-semibold text-white mb-3">Delete Run</h2>
|
||||||
|
<p class="text-sm text-gray-400 mb-4">
|
||||||
|
{"This run failed and can be deleted." if run.status == "failed" else "Delete this run and its associated cache entries."}
|
||||||
|
</p>
|
||||||
|
<div id="delete-result"></div>
|
||||||
|
<button hx-delete="/ui/runs/{run.run_id}/discard" hx-target="#delete-result" hx-swap="innerHTML"
|
||||||
|
hx-confirm="Are you sure you want to delete this run? This cannot be undone."
|
||||||
|
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors">
|
||||||
|
Delete Run
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
output_link = ""
|
output_link = ""
|
||||||
if run.output_hash:
|
if run.output_hash:
|
||||||
output_link = f'''<div class="bg-dark-600 rounded-lg p-4">
|
output_link = f'''<div class="bg-dark-600 rounded-lg p-4">
|
||||||
@@ -661,6 +726,7 @@ async def run_detail(run_id: str, request: Request):
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{publish_html}
|
{publish_html}
|
||||||
|
{delete_html}
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user