Merge branch 'develop' into 'remove-avatar-header'
[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 notifications = Notification.for_user(user, params)
196
197 conn
198 |> put_view(NotificationView)
199 |> render("notification.json", %{notifications: notifications, for: user})
200 end
201
202 def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
203 Notification.set_read_up_to(user, latest_id)
204
205 notifications = Notification.for_user(user, params)
206
207 conn
208 |> put_view(NotificationView)
209 |> render("notification.json", %{notifications: notifications, for: user})
210 end
211
212 def notifications_read(%{assigns: %{user: _user}} = conn, _) do
213 bad_request_reply(conn, "You need to specify latest_id")
214 end
215
216 def follow(%{assigns: %{user: user}} = conn, params) do
217 case TwitterAPI.follow(user, params) do
218 {:ok, user, followed, _activity} ->
219 conn
220 |> put_view(UserView)
221 |> render("show.json", %{user: followed, for: user})
222
223 {:error, msg} ->
224 forbidden_json_reply(conn, msg)
225 end
226 end
227
228 def block(%{assigns: %{user: user}} = conn, params) do
229 case TwitterAPI.block(user, params) do
230 {:ok, user, blocked} ->
231 conn
232 |> put_view(UserView)
233 |> render("show.json", %{user: blocked, for: user})
234
235 {:error, msg} ->
236 forbidden_json_reply(conn, msg)
237 end
238 end
239
240 def unblock(%{assigns: %{user: user}} = conn, params) do
241 case TwitterAPI.unblock(user, params) do
242 {:ok, user, blocked} ->
243 conn
244 |> put_view(UserView)
245 |> render("show.json", %{user: blocked, for: user})
246
247 {:error, msg} ->
248 forbidden_json_reply(conn, msg)
249 end
250 end
251
252 def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
253 with {:ok, activity} <- TwitterAPI.delete(user, id) do
254 conn
255 |> put_view(ActivityView)
256 |> render("activity.json", %{activity: activity, for: user})
257 end
258 end
259
260 def unfollow(%{assigns: %{user: user}} = conn, params) do
261 case TwitterAPI.unfollow(user, params) do
262 {:ok, user, unfollowed} ->
263 conn
264 |> put_view(UserView)
265 |> render("show.json", %{user: unfollowed, for: user})
266
267 {:error, msg} ->
268 forbidden_json_reply(conn, msg)
269 end
270 end
271
272 def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
273 with %Activity{} = activity <- Activity.get_by_id(id),
274 true <- Visibility.visible_for_user?(activity, user) do
275 conn
276 |> put_view(ActivityView)
277 |> render("activity.json", %{activity: activity, for: user})
278 end
279 end
280
281 def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
282 with context when is_binary(context) <- Utils.conversation_id_to_context(id),
283 activities <-
284 ActivityPub.fetch_activities_for_context(context, %{
285 "blocking_user" => user,
286 "user" => user
287 }) do
288 conn
289 |> put_view(ActivityView)
290 |> render("index.json", %{activities: activities, for: user})
291 end
292 end
293
294 @doc """
295 Updates metadata of uploaded media object.
296 Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
297 """
298 def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
299 object = Repo.get(Object, id)
300 description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
301
302 {conn, status, response_body} =
303 cond do
304 !object ->
305 {halt(conn), :not_found, ""}
306
307 !Object.authorize_mutation(object, user) ->
308 {halt(conn), :forbidden, "You can only update your own uploads."}
309
310 !is_binary(description) ->
311 {conn, :not_modified, ""}
312
313 true ->
314 new_data = Map.put(object.data, "name", description)
315
316 {:ok, _} =
317 object
318 |> Object.change(%{data: new_data})
319 |> Repo.update()
320
321 {conn, :no_content, ""}
322 end
323
324 conn
325 |> put_status(status)
326 |> json(response_body)
327 end
328
329 def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
330 response = TwitterAPI.upload(media, user)
331
332 conn
333 |> put_resp_content_type("application/atom+xml")
334 |> send_resp(200, response)
335 end
336
337 def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
338 response = TwitterAPI.upload(media, user, "json")
339
340 conn
341 |> json_reply(200, response)
342 end
343
344 def get_by_id_or_ap_id(id) do
345 activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
346
347 if activity.data["type"] == "Create" do
348 activity
349 else
350 Activity.get_create_by_object_ap_id(activity.data["object"])
351 end
352 end
353
354 def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
355 with {:ok, activity} <- TwitterAPI.fav(user, id) do
356 conn
357 |> put_view(ActivityView)
358 |> render("activity.json", %{activity: activity, for: user})
359 else
360 _ -> json_reply(conn, 400, Jason.encode!(%{}))
361 end
362 end
363
364 def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
365 with {:ok, activity} <- TwitterAPI.unfav(user, id) do
366 conn
367 |> put_view(ActivityView)
368 |> render("activity.json", %{activity: activity, for: user})
369 else
370 _ -> json_reply(conn, 400, Jason.encode!(%{}))
371 end
372 end
373
374 def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
375 with {:ok, activity} <- TwitterAPI.repeat(user, id) do
376 conn
377 |> put_view(ActivityView)
378 |> render("activity.json", %{activity: activity, for: user})
379 else
380 _ -> json_reply(conn, 400, Jason.encode!(%{}))
381 end
382 end
383
384 def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
385 with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do
386 conn
387 |> put_view(ActivityView)
388 |> render("activity.json", %{activity: activity, for: user})
389 else
390 _ -> json_reply(conn, 400, Jason.encode!(%{}))
391 end
392 end
393
394 def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
395 with {:ok, activity} <- TwitterAPI.pin(user, id) do
396 conn
397 |> put_view(ActivityView)
398 |> render("activity.json", %{activity: activity, for: user})
399 else
400 {:error, message} -> bad_request_reply(conn, message)
401 err -> err
402 end
403 end
404
405 def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
406 with {:ok, activity} <- TwitterAPI.unpin(user, id) do
407 conn
408 |> put_view(ActivityView)
409 |> render("activity.json", %{activity: activity, for: user})
410 else
411 {:error, message} -> bad_request_reply(conn, message)
412 err -> err
413 end
414 end
415
416 def register(conn, params) do
417 with {:ok, user} <- TwitterAPI.register_user(params) do
418 conn
419 |> put_view(UserView)
420 |> render("show.json", %{user: user})
421 else
422 {:error, errors} ->
423 conn
424 |> json_reply(400, Jason.encode!(errors))
425 end
426 end
427
428 def password_reset(conn, params) do
429 nickname_or_email = params["email"] || params["nickname"]
430
431 with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
432 json_response(conn, :no_content, "")
433 end
434 end
435
436 def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
437 with %User{} = user <- User.get_cached_by_id(uid),
438 true <- user.local,
439 true <- user.info.confirmation_pending,
440 true <- user.info.confirmation_token == token,
441 info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false),
442 changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
443 {:ok, _} <- User.update_and_set_cache(changeset) do
444 conn
445 |> redirect(to: "/")
446 end
447 end
448
449 def resend_confirmation_email(conn, params) do
450 nickname_or_email = params["email"] || params["nickname"]
451
452 with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
453 {:ok, _} <- User.try_send_confirmation_email(user) do
454 conn
455 |> json_response(:no_content, "")
456 end
457 end
458
459 def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
460 change = Changeset.change(user, %{avatar: nil})
461 {:ok, user} = User.update_and_set_cache(change)
462 CommonAPI.update(user)
463
464 conn
465 |> put_view(UserView)
466 |> render("show.json", %{user: user, for: user})
467 end
468
469 def update_avatar(%{assigns: %{user: user}} = conn, params) do
470 {:ok, object} = ActivityPub.upload(params, type: :avatar)
471 change = Changeset.change(user, %{avatar: object.data})
472 {:ok, user} = User.update_and_set_cache(change)
473 CommonAPI.update(user)
474
475 conn
476 |> put_view(UserView)
477 |> render("show.json", %{user: user, for: user})
478 end
479
480 def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
481 with new_info <- %{"banner" => %{}},
482 info_cng <- User.Info.profile_update(user.info, new_info),
483 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
484 {:ok, user} <- User.update_and_set_cache(changeset) do
485 CommonAPI.update(user)
486 response = %{url: nil} |> Jason.encode!()
487
488 conn
489 |> json_reply(200, response)
490 end
491 end
492
493 def update_banner(%{assigns: %{user: user}} = conn, params) do
494 with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
495 new_info <- %{"banner" => object.data},
496 info_cng <- User.Info.profile_update(user.info, new_info),
497 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
498 {:ok, user} <- User.update_and_set_cache(changeset) do
499 CommonAPI.update(user)
500 %{"url" => [%{"href" => href} | _]} = object.data
501 response = %{url: href} |> Jason.encode!()
502
503 conn
504 |> json_reply(200, response)
505 end
506 end
507
508 def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
509 with new_info <- %{"background" => %{}},
510 info_cng <- User.Info.profile_update(user.info, new_info),
511 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
512 {:ok, _user} <- User.update_and_set_cache(changeset) do
513 response = %{url: nil} |> Jason.encode!()
514
515 conn
516 |> json_reply(200, response)
517 end
518 end
519
520 def update_background(%{assigns: %{user: user}} = conn, params) do
521 with {:ok, object} <- ActivityPub.upload(params, type: :background),
522 new_info <- %{"background" => object.data},
523 info_cng <- User.Info.profile_update(user.info, new_info),
524 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
525 {:ok, _user} <- User.update_and_set_cache(changeset) do
526 %{"url" => [%{"href" => href} | _]} = object.data
527 response = %{url: href} |> Jason.encode!()
528
529 conn
530 |> json_reply(200, response)
531 end
532 end
533
534 def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do
535 with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri),
536 response <- Jason.encode!(user_map) do
537 conn
538 |> json_reply(200, response)
539 else
540 _e ->
541 conn
542 |> put_status(404)
543 |> json(%{error: "Can't find user"})
544 end
545 end
546
547 def followers(%{assigns: %{user: for_user}} = conn, params) do
548 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
549
550 with {:ok, user} <- TwitterAPI.get_user(for_user, params),
551 {:ok, followers} <- User.get_followers(user, page) do
552 followers =
553 cond do
554 for_user && user.id == for_user.id -> followers
555 user.info.hide_followers -> []
556 true -> followers
557 end
558
559 conn
560 |> put_view(UserView)
561 |> render("index.json", %{users: followers, for: conn.assigns[:user]})
562 else
563 _e -> bad_request_reply(conn, "Can't get followers")
564 end
565 end
566
567 def friends(%{assigns: %{user: for_user}} = conn, params) do
568 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
569 {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false)
570
571 page = if export, do: nil, else: page
572
573 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
574 {:ok, friends} <- User.get_friends(user, page) do
575 friends =
576 cond do
577 for_user && user.id == for_user.id -> friends
578 user.info.hide_follows -> []
579 true -> friends
580 end
581
582 conn
583 |> put_view(UserView)
584 |> render("index.json", %{users: friends, for: conn.assigns[:user]})
585 else
586 _e -> bad_request_reply(conn, "Can't get friends")
587 end
588 end
589
590 def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
591 with oauth_tokens <- Token.get_user_tokens(user) do
592 conn
593 |> put_view(TokenView)
594 |> render("index.json", %{tokens: oauth_tokens})
595 end
596 end
597
598 def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
599 Token.delete_user_token(user, id)
600
601 json_reply(conn, 201, "")
602 end
603
604 def blocks(%{assigns: %{user: user}} = conn, _params) do
605 with blocked_users <- User.blocked_users(user) do
606 conn
607 |> put_view(UserView)
608 |> render("index.json", %{users: blocked_users, for: user})
609 end
610 end
611
612 def friend_requests(conn, params) do
613 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
614 {:ok, friend_requests} <- User.get_follow_requests(user) do
615 conn
616 |> put_view(UserView)
617 |> render("index.json", %{users: friend_requests, for: conn.assigns[:user]})
618 else
619 _e -> bad_request_reply(conn, "Can't get friend requests")
620 end
621 end
622
623 def approve_friend_request(conn, %{"user_id" => uid} = _params) do
624 with followed <- conn.assigns[:user],
625 %User{} = follower <- User.get_cached_by_id(uid),
626 {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
627 conn
628 |> put_view(UserView)
629 |> render("show.json", %{user: follower, for: followed})
630 else
631 e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
632 end
633 end
634
635 def deny_friend_request(conn, %{"user_id" => uid} = _params) do
636 with followed <- conn.assigns[:user],
637 %User{} = follower <- User.get_cached_by_id(uid),
638 {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
639 conn
640 |> put_view(UserView)
641 |> render("show.json", %{user: follower, for: followed})
642 else
643 e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
644 end
645 end
646
647 def friends_ids(%{assigns: %{user: user}} = conn, _params) do
648 with {:ok, friends} <- User.get_friends(user) do
649 ids =
650 friends
651 |> Enum.map(fn x -> x.id end)
652 |> Jason.encode!()
653
654 json(conn, ids)
655 else
656 _e -> bad_request_reply(conn, "Can't get friends")
657 end
658 end
659
660 def empty_array(conn, _params) do
661 json(conn, Jason.encode!([]))
662 end
663
664 def raw_empty_array(conn, _params) do
665 json(conn, [])
666 end
667
668 defp build_info_cng(user, params) do
669 info_params =
670 [
671 "no_rich_text",
672 "locked",
673 "hide_followers",
674 "hide_follows",
675 "hide_favorites",
676 "show_role",
677 "skip_thread_containment"
678 ]
679 |> Enum.reduce(%{}, fn key, res ->
680 if value = params[key] do
681 Map.put(res, key, value == "true")
682 else
683 res
684 end
685 end)
686
687 info_params =
688 if value = params["default_scope"] do
689 Map.put(info_params, "default_scope", value)
690 else
691 info_params
692 end
693
694 User.Info.profile_update(user.info, info_params)
695 end
696
697 defp parse_profile_bio(user, params) do
698 if bio = params["description"] do
699 emojis_text = (params["description"] || "") <> " " <> (params["name"] || "")
700
701 emojis =
702 ((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text))
703 |> Enum.dedup()
704
705 user_info =
706 user.info
707 |> Map.put(
708 "emoji",
709 emojis
710 )
711
712 params
713 |> Map.put("bio", User.parse_bio(bio, user))
714 |> Map.put("info", user_info)
715 else
716 params
717 end
718 end
719
720 def update_profile(%{assigns: %{user: user}} = conn, params) do
721 params = parse_profile_bio(user, params)
722 info_cng = build_info_cng(user, params)
723
724 with changeset <- User.update_changeset(user, params),
725 changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
726 {:ok, user} <- User.update_and_set_cache(changeset) do
727 CommonAPI.update(user)
728
729 conn
730 |> put_view(UserView)
731 |> render("user.json", %{user: user, for: user})
732 else
733 error ->
734 Logger.debug("Can't update user: #{inspect(error)}")
735 bad_request_reply(conn, "Can't update user")
736 end
737 end
738
739 def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do
740 activities = TwitterAPI.search(user, params)
741
742 conn
743 |> put_view(ActivityView)
744 |> render("index.json", %{activities: activities, for: user})
745 end
746
747 def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
748 users = User.search(query, resolve: true, for_user: user)
749
750 conn
751 |> put_view(UserView)
752 |> render("index.json", %{users: users, for: user})
753 end
754
755 defp bad_request_reply(conn, error_message) do
756 json = error_json(conn, error_message)
757 json_reply(conn, 400, json)
758 end
759
760 defp json_reply(conn, status, json) do
761 conn
762 |> put_resp_content_type("application/json")
763 |> send_resp(status, json)
764 end
765
766 defp forbidden_json_reply(conn, error_message) do
767 json = error_json(conn, error_message)
768 json_reply(conn, 403, json)
769 end
770
771 def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
772
773 def only_if_public_instance(conn, _) do
774 if Pleroma.Config.get([:instance, :public]) do
775 conn
776 else
777 conn
778 |> forbidden_json_reply("Invalid credentials.")
779 |> halt()
780 end
781 end
782
783 defp error_json(conn, error_message) do
784 %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!()
785 end
786
787 def errors(conn, {:param_cast, _}) do
788 conn
789 |> put_status(400)
790 |> json("Invalid parameters")
791 end
792
793 def errors(conn, _) do
794 conn
795 |> put_status(500)
796 |> json("Something went wrong")
797 end
798 end