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