diff --git a/main.py b/main.py
index e0b259c..e75f9d9 100644
--- a/main.py
+++ b/main.py
@@ -1143,6 +1143,67 @@ async def import_agents_csv(
except Exception as e:
raise HTTPException(status_code=500, detail=f"Import failed: {str(e)}")
+@app.post("/api/admin/agents/delete/csv")
+async def delete_agents_csv(
+ request: Request,
+ file: UploadFile = File(...),
+ current_user: dict = Depends(require_admin)
+):
+ """Delete agents based on CSV file"""
+ if not file.filename.endswith('.csv'):
+ raise HTTPException(status_code=400, detail="File must be a CSV")
+
+ try:
+ contents = await file.read()
+ decoded = contents.decode('utf-8')
+ csv_reader = csv.DictReader(io.StringIO(decoded))
+
+ deleted_count = 0
+ not_found_count = 0
+ error_count = 0
+ errors = []
+
+ for row_num, row in enumerate(csv_reader, start=1):
+ try:
+ agent_id = row.get("agent_id")
+ agent_name = row.get("agent_name")
+
+ if agent_id and agent_id.strip():
+ # Delete by ID
+ success = await crud.delete_agent(agent_id.strip())
+ if success:
+ deleted_count += 1
+ else:
+ not_found_count += 1
+ elif agent_name and agent_name.strip():
+ # Delete by Name (All matches)
+ # We need to find them first
+ # Using direct collection access here for efficiency/custom logic not in crud
+ from database import agents_collection
+ result = await agents_collection.delete_many({"agent_name": agent_name.strip()})
+ if result.deleted_count > 0:
+ deleted_count += result.deleted_count
+ else:
+ not_found_count += 1
+ else:
+ # Skip empty rows
+ continue
+
+ except Exception as e:
+ error_count += 1
+ errors.append(f"Row {row_num}: {str(e)}")
+
+ return JSONResponse({
+ "success": True,
+ "deleted": deleted_count,
+ "not_found": not_found_count,
+ "errors": error_count,
+ "error_details": errors[:10]
+ })
+
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=f"Delete failed: {str(e)}")
+
# Agent Collector API Endpoints (for compatibility with agent_collector app)
@app.post("/agents")
async def create_agent_collector(
diff --git a/templates/nav.html b/templates/nav.html
index 15865ae..8b6c510 100644
--- a/templates/nav.html
+++ b/templates/nav.html
@@ -37,6 +37,11 @@
Import CSV
+
+ Delete by CSV
+
+
{% endif %}
{% endif %}
@@ -235,4 +240,43 @@
// Reset input
input.value = '';
}
+
+ async function deleteCsv(input) {
+ if (!input.files || !input.files[0]) return;
+
+ if (!confirm("WARNING: This will permanently delete all agents found in the CSV file. Are you sure you want to proceed?")) {
+ input.value = '';
+ return;
+ }
+
+ const file = input.files[0];
+ const formData = new FormData();
+ formData.append('file', file);
+
+ try {
+ const response = await fetch('{{ base_path }}/api/admin/agents/delete/csv', {
+ method: 'POST',
+ body: formData
+ });
+
+ const result = await response.json();
+
+ if (response.ok && result.success) {
+ let msg = `Deletion complete!\nDeleted: ${result.deleted}\nNot Found: ${result.not_found}\nErrors: ${result.errors}`;
+ if (result.errors > 0) {
+ msg += `\n\nFirst few errors:\n${result.error_details.join('\n')}`;
+ }
+ alert(msg);
+ window.location.reload();
+ } else {
+ alert('Deletion failed: ' + (result.detail || 'Unknown error'));
+ }
+ } catch (error) {
+ console.error('Error deleting via CSV:', error);
+ alert('Error deleting via CSV: ' + error.message);
+ }
+
+ // Reset input
+ input.value = '';
+ }
\ No newline at end of file