- new bookish/services/database.py: SQLite with WAL mode, tables for bookmarks and reading_positions, keyed by content_hash (SHA256) - new bookish/routes/api.py: REST endpoints for CRUD bookmarks and save/load reading position - upload endpoint returns content_hash (SHA256 of file) - reader route computes SHA256 and passes BOOKISH_HASH to template - reader.js: bookmarks + positions use API server as primary storage with localStorage fallback for offline/resilience - position saves debounced (1.5s) to avoid hammering server - .gitignore: added bookish.db
58 lines
1.6 KiB
Python
58 lines
1.6 KiB
Python
import time
|
|
from flask import Blueprint, jsonify, request, current_app
|
|
from bookish.services.database import (
|
|
save_bookmark,
|
|
get_bookmarks,
|
|
delete_bookmark,
|
|
save_position,
|
|
get_position,
|
|
)
|
|
|
|
api_bp = Blueprint("api", __name__, url_prefix="/api")
|
|
|
|
|
|
# --- Bookmarks ---
|
|
|
|
@api_bp.route("/bookmarks/<content_hash>", methods=["GET"])
|
|
def api_get_bookmarks(content_hash):
|
|
bookmarks = get_bookmarks(content_hash)
|
|
return jsonify({"bookmarks": bookmarks})
|
|
|
|
|
|
@api_bp.route("/bookmarks", methods=["POST"])
|
|
def api_add_bookmark():
|
|
data = request.get_json(silent=True)
|
|
if not data or not data.get("content_hash") or not data.get("id"):
|
|
return jsonify({"error": "Richiesta non valida"}), 400
|
|
|
|
data["created"] = data.get("created", time.time())
|
|
save_bookmark(data)
|
|
return jsonify({"ok": True})
|
|
|
|
|
|
@api_bp.route("/bookmarks/<bookmark_id>", methods=["DELETE"])
|
|
def api_delete_bookmark(bookmark_id):
|
|
delete_bookmark(bookmark_id)
|
|
return jsonify({"ok": True})
|
|
|
|
|
|
# --- Reading positions ---
|
|
|
|
@api_bp.route("/position/<content_hash>", methods=["GET"])
|
|
def api_get_position(content_hash):
|
|
pos = get_position(content_hash)
|
|
if pos:
|
|
return jsonify({"position": pos})
|
|
return jsonify({"position": None})
|
|
|
|
|
|
@api_bp.route("/position/<content_hash>", methods=["PUT"])
|
|
def api_save_position(content_hash):
|
|
data = request.get_json(silent=True)
|
|
if not data:
|
|
return jsonify({"error": "Richiesta non valida"}), 400
|
|
|
|
data["type"] = data.get("type", "unknown")
|
|
save_position(content_hash, data)
|
|
return jsonify({"ok": True})
|