diff --git a/db.py b/db.py
index f65a4de..6ba7cd1 100644
--- a/db.py
+++ b/db.py
@@ -204,5 +204,14 @@ def get_stats() -> dict:
}
+def clear_sync_data() -> dict:
+ """Delete all processed_zips, uploaded_books, and sync_runs rows. Settings are kept."""
+ with get_db() as conn:
+ zips = conn.execute("DELETE FROM processed_zips").rowcount
+ books = conn.execute("DELETE FROM uploaded_books").rowcount
+ runs = conn.execute("DELETE FROM sync_runs").rowcount
+ return {"zips": zips, "books": books, "runs": runs}
+
+
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
diff --git a/main.py b/main.py
index a8677dc..30605e2 100644
--- a/main.py
+++ b/main.py
@@ -174,6 +174,15 @@ async def test_calibre():
return {"ok": ok, "message": message}
+# --- Data reset ---
+
+@app.post("/settings/reset-sync-data")
+async def reset_sync_data():
+ counts = db.clear_sync_data()
+ log.info("Sync data cleared: %s", counts)
+ return RedirectResponse("/settings?reset=1", status_code=303)
+
+
# --- JSON status API ---
@app.get("/api/status")
diff --git a/static/style.css b/static/style.css
index 809821a..efba279 100644
--- a/static/style.css
+++ b/static/style.css
@@ -153,6 +153,9 @@ tr:hover td { background: rgba(255,255,255,0.02); }
.btn-secondary { background: transparent; color: var(--accent); border: 1px solid var(--accent); }
.btn-secondary:hover { background: rgba(99,102,241,0.1); }
.btn-disabled { background: var(--border); color: var(--muted); cursor: not-allowed; }
+.btn-danger { background: #dc2626; color: #fff; border: 1px solid #dc2626; }
+.btn-danger:hover { background: #b91c1c; border-color: #b91c1c; }
+.danger-zone { border-color: rgba(220,38,38,0.4); }
/* Forms */
.form-section {
diff --git a/templates/settings.html b/templates/settings.html
index d69b51e..8f29c94 100644
--- a/templates/settings.html
+++ b/templates/settings.html
@@ -148,6 +148,17 @@
+
+