| """Notebook CRUD service - spec-aligned API, Supabase + storage.""" |
|
|
| import logging |
| from datetime import datetime, timezone |
|
|
| from backend.db import supabase |
| from backend.storage import ensure_notebook_dirs |
|
|
| log = logging.getLogger(__name__) |
|
|
|
|
| def _to_spec(row: dict) -> dict: |
| """Map DB row to spec format.""" |
| return { |
| "notebook_id": str(row["id"]), |
| "name": row["name"], |
| "created_at": row.get("created_at"), |
| } |
|
|
|
|
| def create_notebook(user_id: str, name: str = "Untitled Notebook") -> dict | None: |
| """Create notebook. Returns {notebook_id, name, created_at} or None on error.""" |
| try: |
| data = {"user_id": user_id, "name": name} |
| result = supabase.table("notebooks").insert(data).execute() |
| rows = result.data |
| if not rows: |
| return None |
| row = rows[0] |
| nb_id = str(row["id"]) |
| ensure_notebook_dirs(user_id, nb_id) |
| return _to_spec(row) |
| except Exception as e: |
| log.exception("create_notebook failed") |
| return None |
|
|
|
|
| def list_notebooks(user_id: str) -> list[dict]: |
| """List notebooks for user. Returns [{notebook_id, name, created_at}, ...].""" |
| try: |
| result = ( |
| supabase.table("notebooks") |
| .select("*") |
| .eq("user_id", user_id) |
| .order("updated_at", desc=True) |
| .execute() |
| ) |
| return [_to_spec(r) for r in (result.data or [])] |
| except Exception as e: |
| log.exception("list_notebooks failed") |
| return [] |
|
|
|
|
| def rename_notebook(user_id: str, notebook_id: str, new_name: str) -> bool: |
| """Rename notebook. Returns success.""" |
| try: |
| result = ( |
| supabase.table("notebooks") |
| .update({"name": new_name, "updated_at": datetime.now(timezone.utc).isoformat()}) |
| .eq("id", notebook_id) |
| .eq("user_id", user_id) |
| .execute() |
| ) |
| return len(result.data or []) > 0 |
| except Exception: |
| return False |
|
|
|
|
| def delete_notebook(user_id: str, notebook_id: str) -> bool: |
| """Delete notebook. Returns success.""" |
| try: |
| supabase.table("notebooks").delete().eq("id", notebook_id).eq("user_id", user_id).execute() |
| return True |
| except Exception: |
| return False |
|
|