312 lines
9.3 KiB
Elixir
312 lines
9.3 KiB
Elixir
# Pleroma: A lightweight social networking server
|
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
defmodule Pleroma.Web.PleromaAPI.EventController do
|
|
use Pleroma.Web, :controller
|
|
|
|
import Pleroma.Web.ControllerHelper,
|
|
only: [add_link_headers: 2, json_response: 3, try_render: 3]
|
|
|
|
require Ecto.Query
|
|
|
|
alias Pleroma.Activity
|
|
alias Pleroma.Object
|
|
alias Pleroma.Pagination
|
|
alias Pleroma.Repo
|
|
alias Pleroma.User
|
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
alias Pleroma.Web.ActivityPub.Visibility
|
|
alias Pleroma.Web.CommonAPI
|
|
alias Pleroma.Web.MastodonAPI.AccountView
|
|
alias Pleroma.Web.MastodonAPI.StatusView
|
|
alias Pleroma.Web.PleromaAPI.EventView
|
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
|
alias Pleroma.Web.Plugs.RateLimiter
|
|
|
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
|
|
|
plug(
|
|
:assign_participant
|
|
when action in [:authorize_participation_request, :reject_participation_request]
|
|
)
|
|
|
|
plug(
|
|
:assign_event_activity
|
|
when action in [
|
|
:participations,
|
|
:participation_requests,
|
|
:authorize_participation_request,
|
|
:reject_participation_request,
|
|
:join,
|
|
:leave,
|
|
:export_ics
|
|
]
|
|
)
|
|
|
|
plug(
|
|
OAuthScopesPlug,
|
|
%{scopes: ["write"]}
|
|
when action in [
|
|
:create,
|
|
:update,
|
|
:authorize_participation_request,
|
|
:reject_participation_request,
|
|
:join,
|
|
:leave
|
|
]
|
|
)
|
|
|
|
plug(
|
|
OAuthScopesPlug,
|
|
%{scopes: ["read"]}
|
|
when action in [:participations, :participation_requests, :joined_events]
|
|
)
|
|
|
|
plug(
|
|
OAuthScopesPlug,
|
|
%{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]}
|
|
when action in [:export_ics]
|
|
)
|
|
|
|
@rate_limited_event_actions ~w(create update join leave)a
|
|
|
|
plug(
|
|
RateLimiter,
|
|
[name: :status_id_action, bucket_name: "status_id_action:join_leave", params: [:id]]
|
|
when action in ~w(join leave)a
|
|
)
|
|
|
|
plug(RateLimiter, [name: :events_actions] when action in @rate_limited_event_actions)
|
|
|
|
plug(Pleroma.Web.Plugs.SetApplicationPlug, [] when action in [:create, :update])
|
|
|
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
|
|
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEventOperation
|
|
|
|
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
|
params =
|
|
params
|
|
|> Map.put(:status, Map.get(params, :status, ""))
|
|
|
|
with location <- get_location(params),
|
|
{:ok, activity} <- CommonAPI.event(user, params, location) do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json",
|
|
activity: activity,
|
|
for: user,
|
|
as: :activity
|
|
)
|
|
else
|
|
{:error, {:reject, message}} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{error: message})
|
|
|
|
{:error, message} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{error: message})
|
|
end
|
|
end
|
|
|
|
@doc "PUT /api/v1/pleroma/events/:id"
|
|
def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do
|
|
with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)},
|
|
{_, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
|
{_, true} <- {:is_create, activity.data["type"] == "Create"},
|
|
actor <- Activity.user_actor(activity),
|
|
{_, true} <- {:own_status, actor.id == user.id},
|
|
changes <- body_params |> Map.put(:generator, conn.assigns.application),
|
|
location <- get_location(body_params),
|
|
{_, {:ok, _update_activity}} <-
|
|
{:pipeline, CommonAPI.update_event(user, activity, changes, location)},
|
|
{_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json",
|
|
activity: activity,
|
|
for: user,
|
|
with_direct_conversation_id: true,
|
|
with_muted: Map.get(params, :with_muted, false)
|
|
)
|
|
else
|
|
{:own_status, _} -> {:error, :forbidden}
|
|
{:pipeline, _} -> {:error, :internal_server_error}
|
|
_ -> {:error, :not_found}
|
|
end
|
|
end
|
|
|
|
defp get_location(%{location_id: location_id}) when is_binary(location_id) do
|
|
result = Geospatial.Service.service().get_by_id(location_id)
|
|
|
|
result |> List.first()
|
|
end
|
|
|
|
defp get_location(_), do: nil
|
|
|
|
def participations(%{assigns: %{user: user, event_activity: activity}} = conn, _) do
|
|
with %Object{data: %{"participations" => participations}} <-
|
|
Object.normalize(activity, fetch: false) do
|
|
users =
|
|
User
|
|
|> Ecto.Query.where([u], u.ap_id in ^participations)
|
|
|> Repo.all()
|
|
|> Enum.filter(&(not User.blocks?(user, &1)))
|
|
|
|
conn
|
|
|> put_view(AccountView)
|
|
|> render("index.json", for: user, users: users, as: :user)
|
|
else
|
|
{:visible, false} -> {:error, :not_found}
|
|
_ -> json(conn, [])
|
|
end
|
|
end
|
|
|
|
def participation_requests(
|
|
%{assigns: %{user: %{ap_id: user_ap_id} = for_user, event_activity: activity}} = conn,
|
|
params
|
|
) do
|
|
case activity do
|
|
%Activity{actor: ^user_ap_id, data: %{"object" => ap_id}} ->
|
|
params =
|
|
Map.merge(params, %{
|
|
type: "Join",
|
|
object: ap_id,
|
|
state: "pending",
|
|
skip_preload: true
|
|
})
|
|
|
|
activities =
|
|
[]
|
|
|> ActivityPub.fetch_activities_query(params)
|
|
|> Pagination.fetch_paginated(params)
|
|
|
|
conn
|
|
|> add_link_headers(activities)
|
|
|> put_view(EventView)
|
|
|> render("participation_requests.json",
|
|
activities: activities,
|
|
for: for_user,
|
|
as: :activity
|
|
)
|
|
|
|
%Activity{} ->
|
|
render_error(conn, :forbidden, "Can't get participation requests")
|
|
|
|
{:error, error} ->
|
|
json_response(conn, :bad_request, %{error: error})
|
|
end
|
|
end
|
|
|
|
def join(%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn, _) do
|
|
render_error(conn, :bad_request, "Can't join your own event")
|
|
end
|
|
|
|
def join(
|
|
%{assigns: %{user: user, event_activity: activity}, body_params: params} = conn,
|
|
_
|
|
) do
|
|
with {:ok, _} <- CommonAPI.join(user, activity.id, params) do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json", activity: activity, for: user, as: :activity)
|
|
end
|
|
end
|
|
|
|
def leave(
|
|
%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn,
|
|
_
|
|
) do
|
|
render_error(conn, :bad_request, "Can't leave your own event")
|
|
end
|
|
|
|
def leave(%{assigns: %{user: user, event_activity: activity}} = conn, _) do
|
|
with {:ok, _} <- CommonAPI.leave(user, activity.id) do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json", activity: activity, for: user, as: :activity)
|
|
else
|
|
{:error, error} ->
|
|
json_response(conn, :bad_request, %{error: error})
|
|
end
|
|
end
|
|
|
|
def authorize_participation_request(
|
|
%{
|
|
assigns: %{
|
|
user: for_user,
|
|
participant: participant,
|
|
event_activity: %Activity{data: %{"object" => ap_id}} = activity
|
|
}
|
|
} = conn,
|
|
_
|
|
) do
|
|
with actor <- Activity.user_actor(activity),
|
|
{_, true} <- {:own_event, actor.id == for_user.id},
|
|
{:ok, _} <- CommonAPI.accept_join_request(for_user, participant, ap_id) do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
|
|
else
|
|
{:own_event, _} -> {:error, :forbidden}
|
|
end
|
|
end
|
|
|
|
def reject_participation_request(
|
|
%{
|
|
assigns: %{
|
|
user: for_user,
|
|
participant: participant,
|
|
event_activity: %Activity{data: %{"object" => ap_id}} = activity
|
|
}
|
|
} = conn,
|
|
_
|
|
) do
|
|
with actor <- Activity.user_actor(activity),
|
|
{_, true} <- {:own_event, actor.id == for_user.id},
|
|
{:ok, _} <- CommonAPI.reject_join_request(for_user, participant, ap_id) do
|
|
conn
|
|
|> put_view(StatusView)
|
|
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
|
|
else
|
|
{:own_event, _} -> {:error, :forbidden}
|
|
end
|
|
end
|
|
|
|
def export_ics(%{assigns: %{event_activity: activity}} = conn, _) do
|
|
render(conn, "show.ics", activity: activity)
|
|
end
|
|
|
|
defp assign_participant(%{params: %{participant_id: id}} = conn, _) do
|
|
case User.get_cached_by_id(id) do
|
|
%User{} = participant -> assign(conn, :participant, participant)
|
|
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
|
end
|
|
end
|
|
|
|
defp assign_event_activity(%{assigns: %{user: user}, params: %{id: event_id}} = conn, _) do
|
|
with %Activity{} = activity <- Activity.get_by_id(event_id),
|
|
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)} do
|
|
assign(conn, :event_activity, activity)
|
|
else
|
|
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
|
end
|
|
end
|
|
|
|
def joined_events(%{assigns: %{user: %User{} = user}} = conn, params) do
|
|
activities = ActivityPub.fetch_joined_events(user, params)
|
|
|
|
conn
|
|
|> add_link_headers(activities)
|
|
|> put_view(StatusView)
|
|
|> render("index.json",
|
|
activities: activities,
|
|
for: user,
|
|
as: :activity
|
|
)
|
|
end
|
|
end
|