Pleroma.Web.TwitterAPI.Controller: Remove duplicated alias/es
[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.Notification
13 alias Pleroma.Object
14 alias Pleroma.Repo
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.ActivityPub.Utils
18 alias Pleroma.Web.ActivityPub.Visibility
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.OAuth.Token
21 alias Pleroma.Web.TwitterAPI.ActivityView
22 alias Pleroma.Web.TwitterAPI.NotificationView
23 alias Pleroma.Web.TwitterAPI.TokenView
24 alias Pleroma.Web.TwitterAPI.TwitterAPI
25 alias Pleroma.Web.TwitterAPI.UserView
26
27 require Logger
28
29 plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline])
30 action_fallback(:errors)
31
32 def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
33 token = Phoenix.Token.sign(conn, "user socket", user.id)
34
35 conn
36 |> put_view(UserView)
37 |> render("show.json", %{user: user, token: token, for: user})
38 end
39
40 def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do
41 with media_ids <- extract_media_ids(status_data),
42 {:ok, activity} <-
43 TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do
44 conn
45 |> json(ActivityView.render("activity.json", activity: activity, for: user))
46 else
47 _ -> empty_status_reply(conn)
48 end
49 end
50
51 def status_update(conn, _status_data) do
52 empty_status_reply(conn)
53 end
54
55 defp empty_status_reply(conn) do
56 bad_request_reply(conn, "Client must provide a 'status' parameter with a value.")
57 end
58
59 defp extract_media_ids(status_data) do
60 with media_ids when not is_nil(media_ids) <- status_data["media_ids"],
61 split_ids <- String.split(media_ids, ","),
62 clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do
63 clean_ids
64 else
65 _e -> []
66 end
67 end
68
69 def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do
70 params =
71 params
72 |> Map.put("type", ["Create", "Announce"])
73 |> Map.put("blocking_user", user)
74
75 activities = ActivityPub.fetch_public_activities(params)
76
77 conn
78 |> put_view(ActivityView)
79 |> render("index.json", %{activities: activities, for: user})
80 end
81
82 def public_timeline(%{assigns: %{user: user}} = conn, params) do
83 params =
84 params
85 |> Map.put("type", ["Create", "Announce"])
86 |> Map.put("local_only", true)
87 |> Map.put("blocking_user", user)
88
89 activities = ActivityPub.fetch_public_activities(params)
90
91 conn
92 |> put_view(ActivityView)
93 |> render("index.json", %{activities: activities, for: user})
94 end
95
96 def friends_timeline(%{assigns: %{user: user}} = conn, params) do
97 params =
98 params
99 |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
100 |> Map.put("blocking_user", user)
101 |> Map.put("user", user)
102
103 activities =
104 ActivityPub.fetch_activities([user.ap_id | user.following], params)
105 |> ActivityPub.contain_timeline(user)
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
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 <- Repo.get(Activity, 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) <- TwitterAPI.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 = Repo.get(Activity, 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 <- Repo.get(User, 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, :confirmed),
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, params) do
460 {:ok, object} = ActivityPub.upload(params, type: :avatar)
461 change = Changeset.change(user, %{avatar: object.data})
462 {:ok, user} = User.update_and_set_cache(change)
463 CommonAPI.update(user)
464
465 conn
466 |> put_view(UserView)
467 |> render("show.json", %{user: user, for: user})
468 end
469
470 def update_banner(%{assigns: %{user: user}} = conn, params) do
471 with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
472 new_info <- %{"banner" => object.data},
473 info_cng <- User.Info.profile_update(user.info, new_info),
474 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
475 {:ok, user} <- User.update_and_set_cache(changeset) do
476 CommonAPI.update(user)
477 %{"url" => [%{"href" => href} | _]} = object.data
478 response = %{url: href} |> Jason.encode!()
479
480 conn
481 |> json_reply(200, response)
482 end
483 end
484
485 def update_background(%{assigns: %{user: user}} = conn, params) do
486 with {:ok, object} <- ActivityPub.upload(params, type: :background),
487 new_info <- %{"background" => object.data},
488 info_cng <- User.Info.profile_update(user.info, new_info),
489 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
490 {:ok, _user} <- User.update_and_set_cache(changeset) do
491 %{"url" => [%{"href" => href} | _]} = object.data
492 response = %{url: href} |> Jason.encode!()
493
494 conn
495 |> json_reply(200, response)
496 end
497 end
498
499 def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do
500 with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri),
501 response <- Jason.encode!(user_map) do
502 conn
503 |> json_reply(200, response)
504 else
505 _e ->
506 conn
507 |> put_status(404)
508 |> json(%{error: "Can't find user"})
509 end
510 end
511
512 def followers(%{assigns: %{user: for_user}} = conn, params) do
513 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
514
515 with {:ok, user} <- TwitterAPI.get_user(for_user, params),
516 {:ok, followers} <- User.get_followers(user, page) do
517 followers =
518 cond do
519 for_user && user.id == for_user.id -> followers
520 user.info.hide_followers -> []
521 true -> followers
522 end
523
524 conn
525 |> put_view(UserView)
526 |> render("index.json", %{users: followers, for: conn.assigns[:user]})
527 else
528 _e -> bad_request_reply(conn, "Can't get followers")
529 end
530 end
531
532 def friends(%{assigns: %{user: for_user}} = conn, params) do
533 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
534 {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false)
535
536 page = if export, do: nil, else: page
537
538 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
539 {:ok, friends} <- User.get_friends(user, page) do
540 friends =
541 cond do
542 for_user && user.id == for_user.id -> friends
543 user.info.hide_follows -> []
544 true -> friends
545 end
546
547 conn
548 |> put_view(UserView)
549 |> render("index.json", %{users: friends, for: conn.assigns[:user]})
550 else
551 _e -> bad_request_reply(conn, "Can't get friends")
552 end
553 end
554
555 def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
556 with oauth_tokens <- Token.get_user_tokens(user) do
557 conn
558 |> put_view(TokenView)
559 |> render("index.json", %{tokens: oauth_tokens})
560 end
561 end
562
563 def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
564 Token.delete_user_token(user, id)
565
566 json_reply(conn, 201, "")
567 end
568
569 def blocks(%{assigns: %{user: user}} = conn, _params) do
570 with blocked_users <- User.blocked_users(user) do
571 conn
572 |> put_view(UserView)
573 |> render("index.json", %{users: blocked_users, for: user})
574 end
575 end
576
577 def friend_requests(conn, params) do
578 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
579 {:ok, friend_requests} <- User.get_follow_requests(user) do
580 conn
581 |> put_view(UserView)
582 |> render("index.json", %{users: friend_requests, for: conn.assigns[:user]})
583 else
584 _e -> bad_request_reply(conn, "Can't get friend requests")
585 end
586 end
587
588 def approve_friend_request(conn, %{"user_id" => uid} = _params) do
589 with followed <- conn.assigns[:user],
590 %User{} = follower <- Repo.get(User, uid),
591 {:ok, follower} <- User.maybe_follow(follower, followed),
592 %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
593 {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
594 {:ok, _activity} <-
595 ActivityPub.accept(%{
596 to: [follower.ap_id],
597 actor: followed,
598 object: follow_activity.data["id"],
599 type: "Accept"
600 }) do
601 conn
602 |> put_view(UserView)
603 |> render("show.json", %{user: follower, for: followed})
604 else
605 e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
606 end
607 end
608
609 def deny_friend_request(conn, %{"user_id" => uid} = _params) do
610 with followed <- conn.assigns[:user],
611 %User{} = follower <- Repo.get(User, uid),
612 %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
613 {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
614 {:ok, _activity} <-
615 ActivityPub.reject(%{
616 to: [follower.ap_id],
617 actor: followed,
618 object: follow_activity.data["id"],
619 type: "Reject"
620 }) do
621 conn
622 |> put_view(UserView)
623 |> render("show.json", %{user: follower, for: followed})
624 else
625 e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
626 end
627 end
628
629 def friends_ids(%{assigns: %{user: user}} = conn, _params) do
630 with {:ok, friends} <- User.get_friends(user) do
631 ids =
632 friends
633 |> Enum.map(fn x -> x.id end)
634 |> Jason.encode!()
635
636 json(conn, ids)
637 else
638 _e -> bad_request_reply(conn, "Can't get friends")
639 end
640 end
641
642 def empty_array(conn, _params) do
643 json(conn, Jason.encode!([]))
644 end
645
646 def raw_empty_array(conn, _params) do
647 json(conn, [])
648 end
649
650 defp build_info_cng(user, params) do
651 info_params =
652 ["no_rich_text", "locked", "hide_followers", "hide_follows", "show_role"]
653 |> Enum.reduce(%{}, fn key, res ->
654 if value = params[key] do
655 Map.put(res, key, value == "true")
656 else
657 res
658 end
659 end)
660
661 info_params =
662 if value = params["default_scope"] do
663 Map.put(info_params, "default_scope", value)
664 else
665 info_params
666 end
667
668 User.Info.profile_update(user.info, info_params)
669 end
670
671 defp parse_profile_bio(user, params) do
672 if bio = params["description"] do
673 Map.put(params, "bio", User.parse_bio(bio, user))
674 else
675 params
676 end
677 end
678
679 def update_profile(%{assigns: %{user: user}} = conn, params) do
680 params = parse_profile_bio(user, params)
681 info_cng = build_info_cng(user, params)
682
683 with changeset <- User.update_changeset(user, params),
684 changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
685 {:ok, user} <- User.update_and_set_cache(changeset) do
686 CommonAPI.update(user)
687
688 conn
689 |> put_view(UserView)
690 |> render("user.json", %{user: user, for: user})
691 else
692 error ->
693 Logger.debug("Can't update user: #{inspect(error)}")
694 bad_request_reply(conn, "Can't update user")
695 end
696 end
697
698 def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do
699 activities = TwitterAPI.search(user, params)
700
701 conn
702 |> put_view(ActivityView)
703 |> render("index.json", %{activities: activities, for: user})
704 end
705
706 def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
707 users = User.search(query, resolve: true, for_user: user)
708
709 conn
710 |> put_view(UserView)
711 |> render("index.json", %{users: users, for: user})
712 end
713
714 defp bad_request_reply(conn, error_message) do
715 json = error_json(conn, error_message)
716 json_reply(conn, 400, json)
717 end
718
719 defp json_reply(conn, status, json) do
720 conn
721 |> put_resp_content_type("application/json")
722 |> send_resp(status, json)
723 end
724
725 defp forbidden_json_reply(conn, error_message) do
726 json = error_json(conn, error_message)
727 json_reply(conn, 403, json)
728 end
729
730 def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
731
732 def only_if_public_instance(conn, _) do
733 if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
734 conn
735 else
736 conn
737 |> forbidden_json_reply("Invalid credentials.")
738 |> halt()
739 end
740 end
741
742 defp error_json(conn, error_message) do
743 %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!()
744 end
745
746 def errors(conn, {:param_cast, _}) do
747 conn
748 |> put_status(400)
749 |> json("Invalid parameters")
750 end
751
752 def errors(conn, _) do
753 conn
754 |> put_status(500)
755 |> json("Something went wrong")
756 end
757 end