Add viewer database import and clarify backup actions.

Pairs export with validated .db restore, documents the difference from game
save imports, and renames the JSON upload button to Import game backup.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-19 23:06:59 +02:00
parent defc98dec2
commit 7656578b72
9 changed files with 214 additions and 7 deletions
+32
View File
@@ -49,6 +49,7 @@ from viewers import (
ensure_local_viewer,
get_or_create_cli_viewer,
is_valid_viewer_id,
restore_viewer_db,
viewer_db_path,
)
@@ -270,6 +271,37 @@ def api_delete_goal(viewer_id: str, goal_id: int):
return jsonify({"deleted": True})
@viewer_bp.route("/api/import-viewer", methods=["POST"])
@limiter.limit(IMPORT_LIMIT)
def api_import_viewer(viewer_id: str):
db_path = _resolve_viewer_db(viewer_id)
if "file" not in request.files:
return jsonify({"error": "No file uploaded"}), 400
f = request.files["file"]
if not f.filename:
return jsonify({"error": "No file selected"}), 400
safe_name = secure_filename(f.filename) or "viewer.db"
if not safe_name.lower().endswith(".db"):
return jsonify({"error": "Only .db files are accepted"}), 400
upload_dir = get_data_dir() / "uploads"
upload_dir.mkdir(parents=True, exist_ok=True)
tmp = upload_dir / f"_viewer_db_{viewer_id}_{safe_name}"
f.save(tmp)
try:
stats = restore_viewer_db(tmp, db_path)
except ValueError as exc:
return jsonify({"error": str(exc)}), 400
except OSError:
return jsonify({"error": "Could not restore viewer database"}), 500
finally:
tmp.unlink(missing_ok=True)
return jsonify({"restored": True, **stats})
@viewer_bp.route("/api/import", methods=["POST"])
@limiter.limit(IMPORT_LIMIT)
def api_import(viewer_id: str):