import asyncio from quart import Blueprint, jsonify, request from scribe.auth import get_current_user_id, login_required from scribe.services import sharing as share_svc from scribe.services.access import can_admin_project, can_write_note from scribe.services.notifications import notify_note_shared, notify_project_shared from scribe.services.sharing import list_shared_with_me shares_bp = Blueprint("shares", __name__) # --------------------------------------------------------------------------- # Project shares # --------------------------------------------------------------------------- @shares_bp.route("/api/projects//shares", methods=["GET"]) @login_required async def list_project_shares(project_id: int): uid = get_current_user_id() if not await can_admin_project(uid, project_id): return jsonify({"error": "Forbidden"}), 403 return jsonify({"shares": await share_svc.list_project_shares(project_id)}) @shares_bp.route("/api/projects//shares", methods=["POST"]) @login_required async def create_project_share(project_id: int): uid = get_current_user_id() data = await request.get_json() share = await share_svc.share_project( uid, project_id, target_user_id=data.get("user_id"), target_group_id=data.get("group_id"), permission=data.get("permission", "viewer"), ) if not share: return jsonify({"error": "Forbidden or invalid permission"}), 403 asyncio.create_task(notify_project_shared( project_id, share.permission, uid, data.get("user_id"), data.get("group_id"), )) return jsonify(share.to_dict()), 201 @shares_bp.route("/api/projects//shares/", methods=["PATCH"]) @login_required async def update_project_share(project_id: int, share_id: int): uid = get_current_user_id() data = await request.get_json() share = await share_svc.update_project_share(uid, share_id, data.get("permission", "")) if not share: return jsonify({"error": "Not found or forbidden"}), 404 return jsonify(share.to_dict()) @shares_bp.route("/api/projects//shares/", methods=["DELETE"]) @login_required async def remove_project_share(project_id: int, share_id: int): uid = get_current_user_id() if not await share_svc.remove_project_share(uid, share_id): return jsonify({"error": "Not found or forbidden"}), 404 return jsonify({"status": "ok"}) # --------------------------------------------------------------------------- # Note / task shares # --------------------------------------------------------------------------- @shares_bp.route("/api/notes//shares", methods=["GET"]) @login_required async def list_note_shares(note_id: int): uid = get_current_user_id() if not await can_write_note(uid, note_id): return jsonify({"error": "Forbidden"}), 403 return jsonify({"shares": await share_svc.list_note_shares(note_id)}) @shares_bp.route("/api/notes//shares", methods=["POST"]) @login_required async def create_note_share(note_id: int): uid = get_current_user_id() data = await request.get_json() share = await share_svc.share_note( uid, note_id, target_user_id=data.get("user_id"), target_group_id=data.get("group_id"), permission=data.get("permission", "viewer"), ) if not share: return jsonify({"error": "Forbidden or invalid permission"}), 403 asyncio.create_task(notify_note_shared( note_id, share.permission, uid, data.get("user_id"), data.get("group_id"), )) return jsonify(share.to_dict()), 201 @shares_bp.route("/api/notes//shares/", methods=["PATCH"]) @login_required async def update_note_share(note_id: int, share_id: int): uid = get_current_user_id() data = await request.get_json() share = await share_svc.update_note_share(uid, share_id, data.get("permission", "")) if not share: return jsonify({"error": "Not found or forbidden"}), 404 return jsonify(share.to_dict()) @shares_bp.route("/api/notes//shares/", methods=["DELETE"]) @login_required async def remove_note_share(note_id: int, share_id: int): uid = get_current_user_id() if not await share_svc.remove_note_share(uid, share_id): return jsonify({"error": "Not found or forbidden"}), 404 return jsonify({"status": "ok"}) # --------------------------------------------------------------------------- # Shared-with-me # --------------------------------------------------------------------------- @shares_bp.route("/api/shared-with-me", methods=["GET"]) @login_required async def shared_with_me(): uid = get_current_user_id() return jsonify(await list_shared_with_me(uid))