Merge remote-tracking branch 'pleroma/develop' into feature/addressable-lists
[akkoma] / lib / pleroma / web / twitter_api / twitter_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.TwitterAPI.Controller do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
9
10 alias Ecto.Changeset
11 alias Pleroma.Activity
12 alias Pleroma.Formatter
13 alias Pleroma.Notification
14 alias Pleroma.Object
15 alias Pleroma.Repo
16 alias Pleroma.User
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.ActivityPub.Visibility
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.CommonAPI.Utils
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.TwitterAPI.ActivityView
23 alias Pleroma.Web.TwitterAPI.NotificationView
24 alias Pleroma.Web.TwitterAPI.TokenView
25 alias Pleroma.Web.TwitterAPI.TwitterAPI
26 alias Pleroma.Web.TwitterAPI.UserView
27
28 require Logger
29
30 plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline])
31 action_fallback(:errors)
32
33 def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
34 token = Phoenix.Token.sign(conn, "user socket", user.id)
35
36 conn
37 |> put_view(UserView)
38 |> render("show.json", %{user: user, token: token, for: user})
39 end
40
41 def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do
42 with media_ids <- extract_media_ids(status_data),
43 {:ok, activity} <-
44 TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do
45 conn
46 |> json(ActivityView.render("activity.json", activity: activity, for: user))
47 else
48 _ -> empty_status_reply(conn)
49 end
50 end
51
52 def status_update(conn, _status_data) do
53 empty_status_reply(conn)
54 end
55
56 defp empty_status_reply(conn) do
57 bad_request_reply(conn, "Client must provide a 'status' parameter with a value.")
58 end
59
60 defp extract_media_ids(status_data) do
61 with media_ids when not is_nil(media_ids) <- status_data["media_ids"],
62 split_ids <- String.split(media_ids, ","),
63 clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do
64 clean_ids
65 else
66 _e -> []
67 end
68 end
69
70 def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do
71 params =
72 params
73 |> Map.put("type", ["Create", "Announce"])
74 |> Map.put("blocking_user", user)
75
76 activities = ActivityPub.fetch_public_activities(params)
77
78 conn
79 |> put_view(ActivityView)
80 |> render("index.json", %{activities: activities, for: user})
81 end
82
83 def public_timeline(%{assigns: %{user: user}} = conn, params) do
84 params =
85 params
86 |> Map.put("type", ["Create", "Announce"])
87 |> Map.put("local_only", true)
88 |> Map.put("blocking_user", user)
89
90 activities = ActivityPub.fetch_public_activities(params)
91
92 conn
93 |> put_view(ActivityView)
94 |> render("index.json", %{activities: activities, for: user})
95 end
96
97 def friends_timeline(%{assigns: %{user: user}} = conn, params) do
98 params =
99 params
100 |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
101 |> Map.put("blocking_user", user)
102 |> Map.put("user", user)
103
104 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
105
106 conn
107 |> put_view(ActivityView)
108 |> render("index.json", %{activities: activities, for: user})
109 end
110
111 def show_user(conn, params) do
112 for_user = conn.assigns.user
113
114 with {:ok, shown} <- TwitterAPI.get_user(params),
115 true <-
116 User.auth_active?(shown) ||
117 (for_user && (for_user.id == shown.id || User.superuser?(for_user))) do
118 params =
119 if for_user do
120 %{user: shown, for: for_user}
121 else
122 %{user: shown}
123 end
124
125 conn
126 |> put_view(UserView)
127 |> render("show.json", params)
128 else
129 {:error, msg} ->
130 bad_request_reply(conn, msg)
131
132 false ->
133 conn
134 |> put_status(404)
135 |> json(%{error: "Unconfirmed user"})
136 end
137 end
138
139 def user_timeline(%{assigns: %{user: user}} = conn, params) do
140 case TwitterAPI.get_user(user, params) do
141 {:ok, target_user} ->
142 # Twitter and ActivityPub use a different name and sense for this parameter.
143 {include_rts, params} = Map.pop(params, "include_rts")
144
145 params =
146 case include_rts do
147 x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true")
148 _ -> params
149 end
150
151 activities = ActivityPub.fetch_user_activities(target_user, user, params)
152
153 conn
154 |> put_view(ActivityView)
155 |> render("index.json", %{activities: activities, for: user})
156
157 {:error, msg} ->
158 bad_request_reply(conn, msg)
159 end
160 end
161
162 def mentions_timeline(%{assigns: %{user: user}} = conn, params) do
163 params =
164 params
165 |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
166 |> Map.put("blocking_user", user)
167 |> Map.put(:visibility, ~w[unlisted public private])
168
169 activities = ActivityPub.fetch_activities([user.ap_id], params)
170
171 conn
172 |> put_view(ActivityView)
173 |> render("index.json", %{activities: activities, for: user})
174 end
175
176 def dm_timeline(%{assigns: %{user: user}} = conn, params) do
177 params =
178 params
179 |> Map.put("type", "Create")
180 |> Map.put("blocking_user", user)
181 |> Map.put("user", user)
182 |> Map.put(:visibility, "direct")
183 |> Map.put(:order, :desc)
184
185 activities =
186 ActivityPub.fetch_activities_query([user.ap_id], params)
187 |> Repo.all()
188
189 conn
190 |> put_view(ActivityView)
191 |> render("index.json", %{activities: activities, for: user})
192 end
193
194 def notifications(%{assigns: %{user: user}} = conn, params) do
195 params =
196 if Map.has_key?(params, "with_muted") do
197 Map.put(params, :with_muted, params["with_muted"] in [true, "True", "true", "1"])
198 else
199 params
200 end
201
202 notifications = Notification.for_user(user, params)
203
204 conn
205 |> put_view(NotificationView)
206 |> render("notification.json", %{notifications: notifications, for: user})
207 end
208
209 def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
210 Notification.set_read_up_to(user, latest_id)
211
212 notifications = Notification.for_user(user, params)
213
214 conn
215 |> put_view(NotificationView)
216 |> render("notification.json", %{notifications: notifications, for: user})
217 end
218
219 def notifications_read(%{assigns: %{user: _user}} = conn, _) do
220 bad_request_reply(conn, "You need to specify latest_id")
221 end
222
223 def follow(%{assigns: %{user: user}} = conn, params) do
224 case TwitterAPI.follow(user, params) do
225 {:ok, user, followed, _activity} ->
226 conn
227 |> put_view(UserView)
228 |> render("show.json", %{user: followed, for: user})
229
230 {:error, msg} ->
231 forbidden_json_reply(conn, msg)
232 end
233 end
234
235 def block(%{assigns: %{user: user}} = conn, params) do
236 case TwitterAPI.block(user, params) do
237 {:ok, user, blocked} ->
238 conn
239 |> put_view(UserView)
240 |> render("show.json", %{user: blocked, for: user})
241
242 {:error, msg} ->
243 forbidden_json_reply(conn, msg)
244 end
245 end
246
247 def unblock(%{assigns: %{user: user}} = conn, params) do
248 case TwitterAPI.unblock(user, params) do
249 {:ok, user, blocked} ->
250 conn
251 |> put_view(UserView)
252 |> render("show.json", %{user: blocked, for: user})
253
254 {:error, msg} ->
255 forbidden_json_reply(conn, msg)
256 end
257 end
258
259 def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
260 with {:ok, activity} <- TwitterAPI.delete(user, id) do
261 conn
262 |> put_view(ActivityView)
263 |> render("activity.json", %{activity: activity, for: user})
264 end
265 end
266
267 def unfollow(%{assigns: %{user: user}} = conn, params) do
268 case TwitterAPI.unfollow(user, params) do
269 {:ok, user, unfollowed} ->
270 conn
271 |> put_view(UserView)
272 |> render("show.json", %{user: unfollowed, for: user})
273
274 {:error, msg} ->
275 forbidden_json_reply(conn, msg)
276 end
277 end
278
279 def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
280 with %Activity{} = activity <- Activity.get_by_id(id),
281 true <- Visibility.visible_for_user?(activity, user) do
282 conn
283 |> put_view(ActivityView)
284 |> render("activity.json", %{activity: activity, for: user})
285 end
286 end
287
288 def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
289 with context when is_binary(context) <- Utils.conversation_id_to_context(id),
290 activities <-
291 ActivityPub.fetch_activities_for_context(context, %{
292 "blocking_user" => user,
293 "user" => user
294 }) do
295 conn
296 |> put_view(ActivityView)
297 |> render("index.json", %{activities: activities, for: user})
298 end
299 end
300
301 @doc """
302 Updates metadata of uploaded media object.
303 Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
304 """
305 def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
306 object = Repo.get(Object, id)
307 description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
308
309 {conn, status, response_body} =
310 cond do
311 !object ->
312 {halt(conn), :not_found, ""}
313
314 !Object.authorize_mutation(object, user) ->
315 {halt(conn), :forbidden, "You can only update your own uploads."}
316
317 !is_binary(description) ->
318 {conn, :not_modified, ""}
319
320 true ->
321 new_data = Map.put(object.data, "name", description)
322
323 {:ok, _} =
324 object
325 |> Object.change(%{data: new_data})
326 |> Repo.update()
327
328 {conn, :no_content, ""}
329 end
330
331 conn
332 |> put_status(status)
333 |> json(response_body)
334 end
335
336 def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
337 response = TwitterAPI.upload(media, user)
338
339 conn
340 |> put_resp_content_type("application/atom+xml")
341 |> send_resp(200, response)
342 end
343
344 def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
345 response = TwitterAPI.upload(media, user, "json")
346
347 conn
348 |> json_reply(200, response)
349 end
350
351 def get_by_id_or_ap_id(id) do
352 activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
353
354 if activity.data["type"] == "Create" do
355 activity
356 else
357 Activity.get_create_by_object_ap_id(activity.data["object"])
358 end
359 end
360
361 def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
362 with {:ok, activity} <- TwitterAPI.fav(user, id) do
363 conn
364 |> put_view(ActivityView)
365 |> render("activity.json", %{activity: activity, for: user})
366 else
367 _ -> json_reply(conn, 400, Jason.encode!(%{}))
368 end
369 end
370
371 def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
372 with {:ok, activity} <- TwitterAPI.unfav(user, id) do
373 conn
374 |> put_view(ActivityView)
375 |> render("activity.json", %{activity: activity, for: user})
376 else
377 _ -> json_reply(conn, 400, Jason.encode!(%{}))
378 end
379 end
380
381 def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
382 with {:ok, activity} <- TwitterAPI.repeat(user, id) do
383 conn
384 |> put_view(ActivityView)
385 |> render("activity.json", %{activity: activity, for: user})
386 else
387 _ -> json_reply(conn, 400, Jason.encode!(%{}))
388 end
389 end
390
391 def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
392 with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do
393 conn
394 |> put_view(ActivityView)
395 |> render("activity.json", %{activity: activity, for: user})
396 else
397 _ -> json_reply(conn, 400, Jason.encode!(%{}))
398 end
399 end
400
401 def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
402 with {:ok, activity} <- TwitterAPI.pin(user, id) do
403 conn
404 |> put_view(ActivityView)
405 |> render("activity.json", %{activity: activity, for: user})
406 else
407 {:error, message} -> bad_request_reply(conn, message)
408 err -> err
409 end
410 end
411
412 def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
413 with {:ok, activity} <- TwitterAPI.unpin(user, id) do
414 conn
415 |> put_view(ActivityView)
416 |> render("activity.json", %{activity: activity, for: user})
417 else
418 {:error, message} -> bad_request_reply(conn, message)
419 err -> err
420 end
421 end
422
423 def register(conn, params) do
424 with {:ok, user} <- TwitterAPI.register_user(params) do
425 conn
426 |> put_view(UserView)
427 |> render("show.json", %{user: user})
428 else
429 {:error, errors} ->
430 conn
431 |> json_reply(400, Jason.encode!(errors))
432 end
433 end
434
435 def password_reset(conn, params) do
436 nickname_or_email = params["email"] || params["nickname"]
437
438 with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
439 json_response(conn, :no_content, "")
440 end
441 end
442
443 def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
444 with %User{} = user <- User.get_cached_by_id(uid),
445 true <- user.local,
446 true <- user.info.confirmation_pending,
447 true <- user.info.confirmation_token == token,
448 info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false),
449 changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
450 {:ok, _} <- User.update_and_set_cache(changeset) do
451 conn
452 |> redirect(to: "/")
453 end
454 end
455
456 def resend_confirmation_email(conn, params) do
457 nickname_or_email = params["email"] || params["nickname"]
458
459 with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
460 {:ok, _} <- User.try_send_confirmation_email(user) do
461 conn
462 |> json_response(:no_content, "")
463 end
464 end
465
466 def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
467 change = Changeset.change(user, %{avatar: nil})
468 {:ok, user} = User.update_and_set_cache(change)
469 CommonAPI.update(user)
470
471 conn
472 |> put_view(UserView)
473 |> render("show.json", %{user: user, for: user})
474 end
475
476 def update_avatar(%{assigns: %{user: user}} = conn, params) do
477 {:ok, object} = ActivityPub.upload(params, type: :avatar)
478 change = Changeset.change(user, %{avatar: object.data})
479 {:ok, user} = User.update_and_set_cache(change)
480 CommonAPI.update(user)
481
482 conn
483 |> put_view(UserView)
484 |> render("show.json", %{user: user, for: user})
485 end
486
487 def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
488 with new_info <- %{"banner" => %{}},
489 info_cng <- User.Info.profile_update(user.info, new_info),
490 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
491 {:ok, user} <- User.update_and_set_cache(changeset) do
492 CommonAPI.update(user)
493 response = %{url: nil} |> Jason.encode!()
494
495 conn
496 |> json_reply(200, response)
497 end
498 end
499
500 def update_banner(%{assigns: %{user: user}} = conn, params) do
501 with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
502 new_info <- %{"banner" => object.data},
503 info_cng <- User.Info.profile_update(user.info, new_info),
504 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
505 {:ok, user} <- User.update_and_set_cache(changeset) do
506 CommonAPI.update(user)
507 %{"url" => [%{"href" => href} | _]} = object.data
508 response = %{url: href} |> Jason.encode!()
509
510 conn
511 |> json_reply(200, response)
512 end
513 end
514
515 def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
516 with new_info <- %{"background" => %{}},
517 info_cng <- User.Info.profile_update(user.info, new_info),
518 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
519 {:ok, _user} <- User.update_and_set_cache(changeset) do
520 response = %{url: nil} |> Jason.encode!()
521
522 conn
523 |> json_reply(200, response)
524 end
525 end
526
527 def update_background(%{assigns: %{user: user}} = conn, params) do
528 with {:ok, object} <- ActivityPub.upload(params, type: :background),
529 new_info <- %{"background" => object.data},
530 info_cng <- User.Info.profile_update(user.info, new_info),
531 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
532 {:ok, _user} <- User.update_and_set_cache(changeset) do
533 %{"url" => [%{"href" => href} | _]} = object.data
534 response = %{url: href} |> Jason.encode!()
535
536 conn
537 |> json_reply(200, response)
538 end
539 end
540
541 def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do
542 with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri),
543 response <- Jason.encode!(user_map) do
544 conn
545 |> json_reply(200, response)
546 else
547 _e ->
548 conn
549 |> put_status(404)
550 |> json(%{error: "Can't find user"})
551 end
552 end
553
554 def followers(%{assigns: %{user: for_user}} = conn, params) do
555 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
556
557 with {:ok, user} <- TwitterAPI.get_user(for_user, params),
558 {:ok, followers} <- User.get_followers(user, page) do
559 followers =
560 cond do
561 for_user && user.id == for_user.id -> followers
562 user.info.hide_followers -> []
563 true -> followers
564 end
565
566 conn
567 |> put_view(UserView)
568 |> render("index.json", %{users: followers, for: conn.assigns[:user]})
569 else
570 _e -> bad_request_reply(conn, "Can't get followers")
571 end
572 end
573
574 def friends(%{assigns: %{user: for_user}} = conn, params) do
575 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
576 {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false)
577
578 page = if export, do: nil, else: page
579
580 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
581 {:ok, friends} <- User.get_friends(user, page) do
582 friends =
583 cond do
584 for_user && user.id == for_user.id -> friends
585 user.info.hide_follows -> []
586 true -> friends
587 end
588
589 conn
590 |> put_view(UserView)
591 |> render("index.json", %{users: friends, for: conn.assigns[:user]})
592 else
593 _e -> bad_request_reply(conn, "Can't get friends")
594 end
595 end
596
597 def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
598 with oauth_tokens <- Token.get_user_tokens(user) do
599 conn
600 |> put_view(TokenView)
601 |> render("index.json", %{tokens: oauth_tokens})
602 end
603 end
604
605 def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
606 Token.delete_user_token(user, id)
607
608 json_reply(conn, 201, "")
609 end
610
611 def blocks(%{assigns: %{user: user}} = conn, _params) do
612 with blocked_users <- User.blocked_users(user) do
613 conn
614 |> put_view(UserView)
615 |> render("index.json", %{users: blocked_users, for: user})
616 end
617 end
618
619 def friend_requests(conn, params) do
620 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
621 {:ok, friend_requests} <- User.get_follow_requests(user) do
622 conn
623 |> put_view(UserView)
624 |> render("index.json", %{users: friend_requests, for: conn.assigns[:user]})
625 else
626 _e -> bad_request_reply(conn, "Can't get friend requests")
627 end
628 end
629
630 def approve_friend_request(conn, %{"user_id" => uid} = _params) do
631 with followed <- conn.assigns[:user],
632 %User{} = follower <- User.get_cached_by_id(uid),
633 {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
634 conn
635 |> put_view(UserView)
636 |> render("show.json", %{user: follower, for: followed})
637 else
638 e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
639 end
640 end
641
642 def deny_friend_request(conn, %{"user_id" => uid} = _params) do
643 with followed <- conn.assigns[:user],
644 %User{} = follower <- User.get_cached_by_id(uid),
645 {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
646 conn
647 |> put_view(UserView)
648 |> render("show.json", %{user: follower, for: followed})
649 else
650 e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
651 end
652 end
653
654 def friends_ids(%{assigns: %{user: user}} = conn, _params) do
655 with {:ok, friends} <- User.get_friends(user) do
656 ids =
657 friends
658 |> Enum.map(fn x -> x.id end)
659 |> Jason.encode!()
660
661 json(conn, ids)
662 else
663 _e -> bad_request_reply(conn, "Can't get friends")
664 end
665 end
666
667 def empty_array(conn, _params) do
668 json(conn, Jason.encode!([]))
669 end
670
671 def raw_empty_array(conn, _params) do
672 json(conn, [])
673 end
674
675 defp build_info_cng(user, params) do
676 info_params =
677 [
678 "no_rich_text",
679 "locked",
680 "hide_followers",
681 "hide_follows",
682 "hide_favorites",
683 "show_role",
684 "skip_thread_containment"
685 ]
686 |> Enum.reduce(%{}, fn key, res ->
687 if value = params[key] do
688 Map.put(res, key, value == "true")
689 else
690 res
691 end
692 end)
693
694 info_params =
695 if value = params["default_scope"] do
696 Map.put(info_params, "default_scope", value)
697 else
698 info_params
699 end
700
701 User.Info.profile_update(user.info, info_params)
702 end
703
704 defp parse_profile_bio(user, params) do
705 if bio = params["description"] do
706 emojis_text = (params["description"] || "") <> " " <> (params["name"] || "")
707
708 emojis =
709 ((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text))
710 |> Enum.dedup()
711
712 user_info =
713 user.info
714 |> Map.put(
715 "emoji",
716 emojis
717 )
718
719 params
720 |> Map.put("bio", User.parse_bio(bio, user))
721 |> Map.put("info", user_info)
722 else
723 params
724 end
725 end
726
727 def update_profile(%{assigns: %{user: user}} = conn, params) do
728 params = parse_profile_bio(user, params)
729 info_cng = build_info_cng(user, params)
730
731 with changeset <- User.update_changeset(user, params),
732 changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
733 {:ok, user} <- User.update_and_set_cache(changeset) do
734 CommonAPI.update(user)
735
736 conn
737 |> put_view(UserView)
738 |> render("user.json", %{user: user, for: user})
739 else
740 error ->
741 Logger.debug("Can't update user: #{inspect(error)}")
742 bad_request_reply(conn, "Can't update user")
743 end
744 end
745
746 def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do
747 activities = TwitterAPI.search(user, params)
748
749 conn
750 |> put_view(ActivityView)
751 |> render("index.json", %{activities: activities, for: user})
752 end
753
754 def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
755 users = User.search(query, resolve: true, for_user: user)
756
757 conn
758 |> put_view(UserView)
759 |> render("index.json", %{users: users, for: user})
760 end
761
762 defp bad_request_reply(conn, error_message) do
763 json = error_json(conn, error_message)
764 json_reply(conn, 400, json)
765 end
766
767 defp json_reply(conn, status, json) do
768 conn
769 |> put_resp_content_type("application/json")
770 |> send_resp(status, json)
771 end
772
773 defp forbidden_json_reply(conn, error_message) do
774 json = error_json(conn, error_message)
775 json_reply(conn, 403, json)
776 end
777
778 def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
779
780 def only_if_public_instance(conn, _) do
781 if Pleroma.Config.get([:instance, :public]) do
782 conn
783 else
784 conn
785 |> forbidden_json_reply("Invalid credentials.")
786 |> halt()
787 end
788 end
789
790 defp error_json(conn, error_message) do
791 %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!()
792 end
793
794 def errors(conn, {:param_cast, _}) do
795 conn
796 |> put_status(400)
797 |> json("Invalid parameters")
798 end
799
800 def errors(conn, _) do
801 conn
802 |> put_status(500)
803 |> json("Something went wrong")
804 end
805 end