Merge remote-tracking branch 'remotes/origin/develop' into automatic-authentication...
[akkoma] / lib / pleroma / web / pleroma_api / controllers / pleroma_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
9
10 alias Pleroma.Activity
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.ConversationView
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.MastodonAPI.StatusView
22
23 plug(
24 OAuthScopesPlug,
25 %{scopes: ["read:statuses"]}
26 when action in [:conversation, :conversation_statuses]
27 )
28
29 plug(
30 OAuthScopesPlug,
31 %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
32 when action == :emoji_reactions_by
33 )
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["write:statuses"]}
38 when action in [:react_with_emoji, :unreact_with_emoji]
39 )
40
41 plug(
42 OAuthScopesPlug,
43 %{scopes: ["write:conversations"]}
44 when action in [:update_conversation, :mark_conversations_as_read]
45 )
46
47 plug(
48 OAuthScopesPlug,
49 %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
50 )
51
52 def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
53 with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
54 %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
55 Object.normalize(activity) do
56 reactions =
57 emoji_reactions
58 |> Enum.map(fn [emoji, user_ap_ids] ->
59 if params["emoji"] && params["emoji"] != emoji do
60 nil
61 else
62 users =
63 Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
64 |> Enum.filter(& &1)
65
66 %{
67 name: emoji,
68 count: length(users),
69 accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
70 me: !!(user && user.ap_id in user_ap_ids)
71 }
72 end
73 end)
74 |> Enum.filter(& &1)
75
76 conn
77 |> json(reactions)
78 else
79 _e ->
80 conn
81 |> json([])
82 end
83 end
84
85 def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
86 with {:ok, _activity, _object} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
87 activity <- Activity.get_by_id(activity_id) do
88 conn
89 |> put_view(StatusView)
90 |> render("show.json", %{activity: activity, for: user, as: :activity})
91 end
92 end
93
94 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
95 "id" => activity_id,
96 "emoji" => emoji
97 }) do
98 with {:ok, _activity, _object} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji),
99 activity <- Activity.get_by_id(activity_id) do
100 conn
101 |> put_view(StatusView)
102 |> render("show.json", %{activity: activity, for: user, as: :activity})
103 end
104 end
105
106 def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
107 with %Participation{} = participation <- Participation.get(participation_id),
108 true <- user.id == participation.user_id do
109 conn
110 |> put_view(ConversationView)
111 |> render("participation.json", %{participation: participation, for: user})
112 else
113 _error ->
114 conn
115 |> put_status(404)
116 |> json(%{"error" => "Unknown conversation id"})
117 end
118 end
119
120 def conversation_statuses(
121 %{assigns: %{user: %{id: user_id} = user}} = conn,
122 %{"id" => participation_id} = params
123 ) do
124 with %Participation{user_id: ^user_id} = participation <-
125 Participation.get(participation_id, preload: [:conversation]) do
126 params =
127 params
128 |> Map.put("blocking_user", user)
129 |> Map.put("muting_user", user)
130 |> Map.put("user", user)
131
132 activities =
133 participation.conversation.ap_id
134 |> ActivityPub.fetch_activities_for_context_query(params)
135 |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
136 |> Enum.reverse()
137
138 conn
139 |> add_link_headers(activities)
140 |> put_view(StatusView)
141 |> render("index.json",
142 activities: activities,
143 for: user,
144 as: :activity,
145 skip_relationships: skip_relationships?(params)
146 )
147 else
148 _error ->
149 conn
150 |> put_status(404)
151 |> json(%{"error" => "Unknown conversation id"})
152 end
153 end
154
155 def update_conversation(
156 %{assigns: %{user: user}} = conn,
157 %{"id" => participation_id, "recipients" => recipients}
158 ) do
159 with %Participation{} = participation <- Participation.get(participation_id),
160 true <- user.id == participation.user_id,
161 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
162 conn
163 |> put_view(ConversationView)
164 |> render("participation.json", %{participation: participation, for: user})
165 else
166 {:error, message} ->
167 conn
168 |> put_status(:bad_request)
169 |> json(%{"error" => message})
170
171 _error ->
172 conn
173 |> put_status(404)
174 |> json(%{"error" => "Unknown conversation id"})
175 end
176 end
177
178 def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
179 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
180 conn
181 |> add_link_headers(participations)
182 |> put_view(ConversationView)
183 |> render("participations.json", participations: participations, for: user)
184 end
185 end
186
187 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
188 with {:ok, notification} <- Notification.read_one(user, notification_id) do
189 conn
190 |> put_view(NotificationView)
191 |> render("show.json", %{notification: notification, for: user})
192 else
193 {:error, message} ->
194 conn
195 |> put_status(:bad_request)
196 |> json(%{"error" => message})
197 end
198 end
199
200 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
201 with notifications <- Notification.set_read_up_to(user, max_id) do
202 notifications = Enum.take(notifications, 80)
203
204 conn
205 |> put_view(NotificationView)
206 |> render("index.json",
207 notifications: notifications,
208 for: user,
209 skip_relationships: skip_relationships?(params)
210 )
211 end
212 end
213 end