Something something like soundcloud but not like soundcloud.
Log in, upload records, done.
Simple, easy, KISS.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

activitypub.py 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. from flask import Blueprint, request, abort, current_app, Response, jsonify, flash, render_template, redirect, url_for
  2. from little_boxes import activitypub
  3. from little_boxes.httpsig import verify_request
  4. from activitypub.backend import post_to_inbox, Box
  5. from activitypub.utils import activity_from_doc, build_ordered_collection
  6. from models import Activity, User
  7. from flask_accept import accept_fallback
  8. from flask_babelex import gettext
  9. bp_ap = Blueprint("bp_ap", __name__)
  10. @bp_ap.route("/user/<string:name>/inbox", methods=["GET", "POST"])
  11. def user_inbox(name):
  12. be = activitypub.get_backend()
  13. if not be:
  14. abort(500)
  15. data = request.get_json(force=True)
  16. if not data:
  17. abort(500)
  18. current_app.logger.debug(f"req_headers={request.headers}")
  19. current_app.logger.debug(f"raw_data={data}")
  20. try:
  21. if not verify_request(request.method, request.path, request.headers, request.data):
  22. raise Exception("failed to verify request")
  23. except Exception:
  24. current_app.logger.exception("failed to verify request")
  25. try:
  26. data = be.fetch_iri(data["id"])
  27. except Exception:
  28. current_app.logger.exception(f"failed to fetch remote id " f"at {data['id']}")
  29. resp = {"error": "failed to verify request " "(using HTTP signatures or fetching the IRI)"}
  30. response = jsonify(resp)
  31. response.mimetype = "application/json; charset=utf-8"
  32. response.status = 422
  33. return response
  34. activity = activitypub.parse_activity(data)
  35. current_app.logger.debug(f"inbox_activity={activity}/{data}")
  36. post_to_inbox(activity)
  37. return Response(status=201)
  38. @bp_ap.route("/user/<string:name>/outbox", methods=["GET", "POST"])
  39. def user_outbox(name):
  40. be = activitypub.get_backend()
  41. if not be:
  42. abort(500)
  43. data = request.get_json(force=True)
  44. if not data:
  45. abort(500)
  46. current_app.logger.debug(f"req_headers={request.headers}")
  47. current_app.logger.debug(f"raw_data={data}")
  48. @bp_ap.route("/user/<string:name>/followings", methods=["GET"])
  49. @accept_fallback
  50. def followings(name):
  51. pcfg = {"title": gettext("%(username)s' followings", username=name)}
  52. user = User.query.filter(User.name == name).first()
  53. if not user:
  54. flash(gettext("User not found"), "error")
  55. return redirect(url_for("bp_main.home"))
  56. followings = user.actor[0].followings
  57. return render_template("users/followings.jinja2", pcfg=pcfg, user=user, actor=user.actor[0], followings=followings)
  58. @bp_ap.route("/user/<string:name>/followers", methods=["GET"])
  59. @accept_fallback
  60. def followers(name):
  61. pcfg = {"title": gettext("%(username)s' followers", username=name)}
  62. user = User.query.filter(User.name == name).first()
  63. if not user:
  64. flash(gettext("User not found"), "error")
  65. return redirect(url_for("bp_main.home"))
  66. return render_template(
  67. "users/followers.jinja2", pcfg=pcfg, user=user, actor=user.actor[0], followers=user.actor[0].followers
  68. )
  69. @bp_ap.route("/user/<string:name>/followers", methods=["GET", "POST"])
  70. @followers.support("application/json", "application/activity+json")
  71. def user_followers(name):
  72. be = activitypub.get_backend()
  73. if not be:
  74. abort(500)
  75. # data = request.get_json(force=True)
  76. # if not data:
  77. # abort(500)
  78. current_app.logger.debug(f"req_headers={request.headers}")
  79. # current_app.logger.debug(f"raw_data={data}")
  80. user = User.query.filter(User.name == name).first()
  81. if not user:
  82. abort(404)
  83. actor = user.actor[0]
  84. followers = actor.followers
  85. return jsonify(**build_ordered_collection(followers, actor.url, request.args.get("page")))
  86. @bp_ap.route("/inbox", methods=["GET", "POST"])
  87. def inbox():
  88. be = activitypub.get_backend()
  89. if not be:
  90. abort(500)
  91. data = request.get_json(force=True)
  92. if not data:
  93. abort(500)
  94. current_app.logger.debug(f"req_headers={request.headers}")
  95. current_app.logger.debug(f"raw_data={data}")
  96. try:
  97. if not verify_request(request.method, request.path, request.headers, request.data):
  98. raise Exception("failed to verify request")
  99. except Exception:
  100. current_app.logger.exception("failed to verify request")
  101. try:
  102. data = be.fetch_iri(data["id"])
  103. except Exception:
  104. current_app.logger.exception(f"failed to fetch remote id " f"at {data['id']}")
  105. resp = {"error": "failed to verify request " "(using HTTP signatures or fetching the IRI)"}
  106. response = jsonify(resp)
  107. response.mimetype = "application/json; charset=utf-8"
  108. response.status = 422
  109. return response
  110. activity = activitypub.parse_activity(data)
  111. current_app.logger.debug(f"inbox_activity={activity}/{data}")
  112. post_to_inbox(activity)
  113. return Response(status=201)
  114. @bp_ap.route("/outbox", methods=["GET", "POST"])
  115. def outbox():
  116. be = activitypub.get_backend()
  117. if not be:
  118. abort(500)
  119. data = request.get_json(force=True)
  120. if not data:
  121. abort(500)
  122. current_app.logger.debug(f"req_headers={request.headers}")
  123. current_app.logger.debug(f"raw_data={data}")
  124. @bp_ap.route("/outbox/<string:item_id>", methods=["GET", "POST"])
  125. def outbox_item(item_id):
  126. be = activitypub.get_backend()
  127. if not be:
  128. abort(500)
  129. # data = request.get_json()
  130. # if not data:
  131. # abort(500)
  132. current_app.logger.debug(f"req_headers={request.headers}")
  133. # current_app.logger.debug(f"raw_data={data}")
  134. current_app.logger.debug(f"activity url {be.activity_url(item_id)}")
  135. item = Activity.query.filter(Activity.box == Box.OUTBOX.value, Activity.url == be.activity_url(item_id)).first()
  136. if not item:
  137. abort(404)
  138. # check if deleted, if yes, return 410 tombstone gone
  139. current_app.logger.debug(f"item payload=={item.payload}")
  140. return jsonify(**activity_from_doc(item.payload))