logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

poll_controller.ex (3267B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MastodonAPI.PollController do
  5. use Pleroma.Web, :controller
  6. import Pleroma.Web.ControllerHelper, only: [try_render: 3, json_response: 3]
  7. alias Pleroma.Activity
  8. alias Pleroma.Object
  9. alias Pleroma.Web.ActivityPub.Visibility
  10. alias Pleroma.Web.CommonAPI
  11. alias Pleroma.Web.Plugs.OAuthScopesPlug
  12. alias Pleroma.Workers.PollWorker
  13. action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
  14. plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
  15. plug(
  16. OAuthScopesPlug,
  17. %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :show
  18. )
  19. plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote)
  20. defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
  21. @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
  22. @poll_refresh_interval 120
  23. @doc "GET /api/v1/polls/:id"
  24. def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
  25. with %Object{} = object <- Object.get_by_id(id),
  26. %Activity{} = activity <-
  27. Activity.get_create_by_object_ap_id_with_object(object.data["id"]),
  28. true <- Visibility.visible_for_user?(activity, user) do
  29. maybe_refresh_poll(activity)
  30. try_render(conn, "show.json", %{object: object, for: user})
  31. else
  32. error when is_nil(error) or error == false ->
  33. render_error(conn, :not_found, "Record not found")
  34. end
  35. end
  36. @doc "POST /api/v1/polls/:id/votes"
  37. def vote(
  38. %{
  39. assigns: %{user: user},
  40. private: %{open_api_spex: %{body_params: %{choices: choices}, params: %{id: id}}}
  41. } = conn,
  42. _
  43. ) do
  44. with %Object{data: %{"type" => "Question"}} = object <- Object.get_by_id(id),
  45. %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
  46. true <- Visibility.visible_for_user?(activity, user),
  47. {:ok, _activities, object} <- get_cached_vote_or_vote(object, user, choices) do
  48. try_render(conn, "show.json", %{object: object, for: user})
  49. else
  50. nil -> render_error(conn, :not_found, "Record not found")
  51. false -> render_error(conn, :not_found, "Record not found")
  52. {:error, message} -> json_response(conn, :unprocessable_entity, %{error: message})
  53. end
  54. end
  55. defp get_cached_vote_or_vote(object, user, choices) do
  56. idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
  57. @cachex.fetch!(:idempotency_cache, idempotency_key, fn _ ->
  58. case CommonAPI.vote(object, user, choices) do
  59. {:error, _message} = res -> {:ignore, res}
  60. res -> {:commit, res}
  61. end
  62. end)
  63. end
  64. defp maybe_refresh_poll(%Activity{object: %Object{} = object} = activity) do
  65. with false <- activity.local,
  66. {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]),
  67. {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do
  68. PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
  69. |> Oban.insert(unique: [period: @poll_refresh_interval])
  70. end
  71. end
  72. end