Update CHANGELOG
[akkoma] / lib / pleroma / web / admin_api / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
7 alias Pleroma.Activity
8 alias Pleroma.User
9 alias Pleroma.UserInviteToken
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.Relay
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.AdminAPI.Config
14 alias Pleroma.Web.AdminAPI.ConfigView
15 alias Pleroma.Web.AdminAPI.ReportView
16 alias Pleroma.Web.AdminAPI.Search
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.StatusView
19
20 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
21
22 require Logger
23
24 @users_page_size 50
25
26 action_fallback(:errors)
27
28 def user_delete(conn, %{"nickname" => nickname}) do
29 User.get_cached_by_nickname(nickname)
30 |> User.delete()
31
32 conn
33 |> json(nickname)
34 end
35
36 def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
37 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
38 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
39 User.follow(follower, followed)
40 end
41
42 conn
43 |> json("ok")
44 end
45
46 def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
47 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
48 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
49 User.unfollow(follower, followed)
50 end
51
52 conn
53 |> json("ok")
54 end
55
56 def user_create(
57 conn,
58 %{"nickname" => nickname, "email" => email, "password" => password}
59 ) do
60 user_data = %{
61 nickname: nickname,
62 name: nickname,
63 email: email,
64 password: password,
65 password_confirmation: password,
66 bio: "."
67 }
68
69 changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
70 {:ok, user} = User.register(changeset)
71
72 conn
73 |> json(user.nickname)
74 end
75
76 def user_show(conn, %{"nickname" => nickname}) do
77 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
78 conn
79 |> json(AccountView.render("show.json", %{user: user}))
80 else
81 _ -> {:error, :not_found}
82 end
83 end
84
85 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
86 godmode = params["godmode"] == "true" || params["godmode"] == true
87
88 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
89 {_, page_size} = page_params(params)
90
91 activities =
92 ActivityPub.fetch_user_activities(user, nil, %{
93 "limit" => page_size,
94 "godmode" => godmode
95 })
96
97 conn
98 |> json(StatusView.render("index.json", %{activities: activities, as: :activity}))
99 else
100 _ -> {:error, :not_found}
101 end
102 end
103
104 def user_toggle_activation(conn, %{"nickname" => nickname}) do
105 user = User.get_cached_by_nickname(nickname)
106
107 {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
108
109 conn
110 |> json(AccountView.render("show.json", %{user: updated_user}))
111 end
112
113 def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
114 with {:ok, _} <- User.tag(nicknames, tags),
115 do: json_response(conn, :no_content, "")
116 end
117
118 def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
119 with {:ok, _} <- User.untag(nicknames, tags),
120 do: json_response(conn, :no_content, "")
121 end
122
123 def list_users(conn, params) do
124 {page, page_size} = page_params(params)
125 filters = maybe_parse_filters(params["filters"])
126
127 search_params = %{
128 query: params["query"],
129 page: page,
130 page_size: page_size,
131 tags: params["tags"],
132 name: params["name"],
133 email: params["email"]
134 }
135
136 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
137 do:
138 conn
139 |> json(
140 AccountView.render("index.json",
141 users: users,
142 count: count,
143 page_size: page_size
144 )
145 )
146 end
147
148 @filters ~w(local external active deactivated is_admin is_moderator)
149
150 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
151 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
152
153 defp maybe_parse_filters(filters) do
154 filters
155 |> String.split(",")
156 |> Enum.filter(&Enum.member?(@filters, &1))
157 |> Enum.map(&String.to_atom(&1))
158 |> Enum.into(%{}, &{&1, true})
159 end
160
161 def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
162 when permission_group in ["moderator", "admin"] do
163 user = User.get_cached_by_nickname(nickname)
164
165 info =
166 %{}
167 |> Map.put("is_" <> permission_group, true)
168
169 info_cng = User.Info.admin_api_update(user.info, info)
170
171 cng =
172 user
173 |> Ecto.Changeset.change()
174 |> Ecto.Changeset.put_embed(:info, info_cng)
175
176 {:ok, _user} = User.update_and_set_cache(cng)
177
178 json(conn, info)
179 end
180
181 def right_add(conn, _) do
182 render_error(conn, :not_found, "No such permission_group")
183 end
184
185 def right_get(conn, %{"nickname" => nickname}) do
186 user = User.get_cached_by_nickname(nickname)
187
188 conn
189 |> json(%{
190 is_moderator: user.info.is_moderator,
191 is_admin: user.info.is_admin
192 })
193 end
194
195 def right_delete(
196 %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
197 %{
198 "permission_group" => permission_group,
199 "nickname" => nickname
200 }
201 )
202 when permission_group in ["moderator", "admin"] do
203 if admin_nickname == nickname do
204 render_error(conn, :forbidden, "You can't revoke your own admin status.")
205 else
206 user = User.get_cached_by_nickname(nickname)
207
208 info =
209 %{}
210 |> Map.put("is_" <> permission_group, false)
211
212 info_cng = User.Info.admin_api_update(user.info, info)
213
214 cng =
215 Ecto.Changeset.change(user)
216 |> Ecto.Changeset.put_embed(:info, info_cng)
217
218 {:ok, _user} = User.update_and_set_cache(cng)
219
220 json(conn, info)
221 end
222 end
223
224 def right_delete(conn, _) do
225 render_error(conn, :not_found, "No such permission_group")
226 end
227
228 def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
229 with {:ok, status} <- Ecto.Type.cast(:boolean, status),
230 %User{} = user <- User.get_cached_by_nickname(nickname),
231 {:ok, _} <- User.deactivate(user, !status),
232 do: json_response(conn, :no_content, "")
233 end
234
235 def relay_follow(conn, %{"relay_url" => target}) do
236 with {:ok, _message} <- Relay.follow(target) do
237 json(conn, target)
238 else
239 _ ->
240 conn
241 |> put_status(500)
242 |> json(target)
243 end
244 end
245
246 def relay_unfollow(conn, %{"relay_url" => target}) do
247 with {:ok, _message} <- Relay.unfollow(target) do
248 json(conn, target)
249 else
250 _ ->
251 conn
252 |> put_status(500)
253 |> json(target)
254 end
255 end
256
257 @doc "Sends registration invite via email"
258 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
259 with true <-
260 Pleroma.Config.get([:instance, :invites_enabled]) &&
261 !Pleroma.Config.get([:instance, :registrations_open]),
262 {:ok, invite_token} <- UserInviteToken.create_invite(),
263 email <-
264 Pleroma.Emails.UserEmail.user_invitation_email(
265 user,
266 invite_token,
267 email,
268 params["name"]
269 ),
270 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
271 json_response(conn, :no_content, "")
272 end
273 end
274
275 @doc "Get a account registeration invite token (base64 string)"
276 def get_invite_token(conn, params) do
277 options = params["invite"] || %{}
278 {:ok, invite} = UserInviteToken.create_invite(options)
279
280 conn
281 |> json(invite.token)
282 end
283
284 @doc "Get list of created invites"
285 def invites(conn, _params) do
286 invites = UserInviteToken.list_invites()
287
288 conn
289 |> json(AccountView.render("invites.json", %{invites: invites}))
290 end
291
292 @doc "Revokes invite by token"
293 def revoke_invite(conn, %{"token" => token}) do
294 with {:ok, invite} <- UserInviteToken.find_by_token(token),
295 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
296 conn
297 |> json(AccountView.render("invite.json", %{invite: updated_invite}))
298 else
299 nil -> {:error, :not_found}
300 end
301 end
302
303 @doc "Get a password reset token (base64 string) for given nickname"
304 def get_password_reset(conn, %{"nickname" => nickname}) do
305 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
306 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
307
308 conn
309 |> json(token.token)
310 end
311
312 def list_reports(conn, params) do
313 params =
314 params
315 |> Map.put("type", "Flag")
316 |> Map.put("skip_preload", true)
317
318 reports =
319 []
320 |> ActivityPub.fetch_activities(params)
321 |> Enum.reverse()
322
323 conn
324 |> put_view(ReportView)
325 |> render("index.json", %{reports: reports})
326 end
327
328 def report_show(conn, %{"id" => id}) do
329 with %Activity{} = report <- Activity.get_by_id(id) do
330 conn
331 |> put_view(ReportView)
332 |> render("show.json", %{report: report})
333 else
334 _ -> {:error, :not_found}
335 end
336 end
337
338 def report_update_state(conn, %{"id" => id, "state" => state}) do
339 with {:ok, report} <- CommonAPI.update_report_state(id, state) do
340 conn
341 |> put_view(ReportView)
342 |> render("show.json", %{report: report})
343 end
344 end
345
346 def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
347 with false <- is_nil(params["status"]),
348 %Activity{} <- Activity.get_by_id(id) do
349 params =
350 params
351 |> Map.put("in_reply_to_status_id", id)
352 |> Map.put("visibility", "direct")
353
354 {:ok, activity} = CommonAPI.post(user, params)
355
356 conn
357 |> put_view(StatusView)
358 |> render("status.json", %{activity: activity})
359 else
360 true ->
361 {:param_cast, nil}
362
363 nil ->
364 {:error, :not_found}
365 end
366 end
367
368 def status_update(conn, %{"id" => id} = params) do
369 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
370 conn
371 |> put_view(StatusView)
372 |> render("status.json", %{activity: activity})
373 end
374 end
375
376 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
377 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
378 json(conn, %{})
379 end
380 end
381
382 def migrate_to_db(conn, _params) do
383 Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
384 json(conn, %{})
385 end
386
387 def migrate_from_db(conn, _params) do
388 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
389 json(conn, %{})
390 end
391
392 def config_show(conn, _params) do
393 configs = Pleroma.Repo.all(Config)
394
395 conn
396 |> put_view(ConfigView)
397 |> render("index.json", %{configs: configs})
398 end
399
400 def config_update(conn, %{"configs" => configs}) do
401 updated =
402 if Pleroma.Config.get([:instance, :dynamic_configuration]) do
403 updated =
404 Enum.map(configs, fn
405 %{"group" => group, "key" => key, "delete" => "true"} ->
406 {:ok, _} = Config.delete(%{group: group, key: key})
407 nil
408
409 %{"group" => group, "key" => key, "value" => value} ->
410 {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
411 config
412 end)
413 |> Enum.reject(&is_nil(&1))
414
415 Pleroma.Config.TransferTask.load_and_update_env()
416 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
417 updated
418 else
419 []
420 end
421
422 conn
423 |> put_view(ConfigView)
424 |> render("index.json", %{configs: updated})
425 end
426
427 def errors(conn, {:error, :not_found}) do
428 conn
429 |> put_status(:not_found)
430 |> json(dgettext("errors", "Not found"))
431 end
432
433 def errors(conn, {:error, reason}) do
434 conn
435 |> put_status(:bad_request)
436 |> json(reason)
437 end
438
439 def errors(conn, {:param_cast, _}) do
440 conn
441 |> put_status(:bad_request)
442 |> json(dgettext("errors", "Invalid parameters"))
443 end
444
445 def errors(conn, _) do
446 conn
447 |> put_status(:internal_server_error)
448 |> json(dgettext("errors", "Something went wrong"))
449 end
450
451 defp page_params(params) do
452 {get_page(params["page"]), get_page_size(params["page_size"])}
453 end
454
455 defp get_page(page_string) when is_nil(page_string), do: 1
456
457 defp get_page(page_string) do
458 case Integer.parse(page_string) do
459 {page, _} -> page
460 :error -> 1
461 end
462 end
463
464 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
465
466 defp get_page_size(page_size_string) do
467 case Integer.parse(page_size_string) do
468 {page_size, _} -> page_size
469 :error -> @users_page_size
470 end
471 end
472 end