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