Merge branch 'card-handling' 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.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView, TokenView}
12 alias Pleroma.Web.CommonAPI
13 alias Pleroma.{Repo, Activity, Object, User, Notification}
14 alias Pleroma.Web.OAuth.Token
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.Visibility
17 alias Pleroma.Web.ActivityPub.Utils
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.TwitterAPI.ActivityView
20 alias Pleroma.Web.TwitterAPI.NotificationView
21 alias Pleroma.Web.TwitterAPI.TwitterAPI
22 alias Pleroma.Web.TwitterAPI.UserView
23 alias Pleroma.Activity
24 alias Pleroma.Object
25 alias Pleroma.Notification
26 alias Pleroma.Repo
27 alias Pleroma.User
28
29 require Logger
30
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 =
106 ActivityPub.fetch_activities([user.ap_id | user.following], params)
107 |> ActivityPub.contain_timeline(user)
108
109 conn
110 |> put_view(ActivityView)
111 |> render("index.json", %{activities: activities, for: user})
112 end
113
114 def show_user(conn, params) do
115 for_user = conn.assigns.user
116
117 with {:ok, shown} <- TwitterAPI.get_user(params),
118 true <-
119 User.auth_active?(shown) ||
120 (for_user && (for_user.id == shown.id || User.superuser?(for_user))) do
121 params =
122 if for_user do
123 %{user: shown, for: for_user}
124 else
125 %{user: shown}
126 end
127
128 conn
129 |> put_view(UserView)
130 |> render("show.json", params)
131 else
132 {:error, msg} ->
133 bad_request_reply(conn, msg)
134
135 false ->
136 conn
137 |> put_status(404)
138 |> json(%{error: "Unconfirmed user"})
139 end
140 end
141
142 def user_timeline(%{assigns: %{user: user}} = conn, params) do
143 case TwitterAPI.get_user(user, params) do
144 {:ok, target_user} ->
145 # Twitter and ActivityPub use a different name and sense for this parameter.
146 {include_rts, params} = Map.pop(params, "include_rts")
147
148 params =
149 case include_rts do
150 x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true")
151 _ -> params
152 end
153
154 activities = ActivityPub.fetch_user_activities(target_user, user, params)
155
156 conn
157 |> put_view(ActivityView)
158 |> render("index.json", %{activities: activities, for: user})
159
160 {:error, msg} ->
161 bad_request_reply(conn, msg)
162 end
163 end
164
165 def mentions_timeline(%{assigns: %{user: user}} = conn, params) do
166 params =
167 params
168 |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
169 |> Map.put("blocking_user", user)
170
171 activities = ActivityPub.fetch_activities([user.ap_id], params)
172
173 conn
174 |> put_view(ActivityView)
175 |> render("index.json", %{activities: activities, for: user})
176 end
177
178 def dm_timeline(%{assigns: %{user: user}} = conn, params) do
179 query =
180 ActivityPub.fetch_activities_query(
181 [user.ap_id],
182 Map.merge(params, %{"type" => "Create", "user" => user, visibility: "direct"})
183 )
184
185 activities = Repo.all(query)
186
187 conn
188 |> put_view(ActivityView)
189 |> render("index.json", %{activities: activities, for: user})
190 end
191
192 def notifications(%{assigns: %{user: user}} = conn, params) do
193 notifications = Notification.for_user(user, params)
194
195 conn
196 |> put_view(NotificationView)
197 |> render("notification.json", %{notifications: notifications, for: user})
198 end
199
200 def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
201 Notification.set_read_up_to(user, latest_id)
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, _) do
211 bad_request_reply(conn, "You need to specify latest_id")
212 end
213
214 def follow(%{assigns: %{user: user}} = conn, params) do
215 case TwitterAPI.follow(user, params) do
216 {:ok, user, followed, _activity} ->
217 conn
218 |> put_view(UserView)
219 |> render("show.json", %{user: followed, for: user})
220
221 {:error, msg} ->
222 forbidden_json_reply(conn, msg)
223 end
224 end
225
226 def block(%{assigns: %{user: user}} = conn, params) do
227 case TwitterAPI.block(user, params) do
228 {:ok, user, blocked} ->
229 conn
230 |> put_view(UserView)
231 |> render("show.json", %{user: blocked, for: user})
232
233 {:error, msg} ->
234 forbidden_json_reply(conn, msg)
235 end
236 end
237
238 def unblock(%{assigns: %{user: user}} = conn, params) do
239 case TwitterAPI.unblock(user, params) do
240 {:ok, user, blocked} ->
241 conn
242 |> put_view(UserView)
243 |> render("show.json", %{user: blocked, for: user})
244
245 {:error, msg} ->
246 forbidden_json_reply(conn, msg)
247 end
248 end
249
250 def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
251 with {:ok, activity} <- TwitterAPI.delete(user, id) do
252 conn
253 |> put_view(ActivityView)
254 |> render("activity.json", %{activity: activity, for: user})
255 end
256 end
257
258 def unfollow(%{assigns: %{user: user}} = conn, params) do
259 case TwitterAPI.unfollow(user, params) do
260 {:ok, user, unfollowed} ->
261 conn
262 |> put_view(UserView)
263 |> render("show.json", %{user: unfollowed, for: user})
264
265 {:error, msg} ->
266 forbidden_json_reply(conn, msg)
267 end
268 end
269
270 def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
271 with %Activity{} = activity <- Repo.get(Activity, id),
272 true <- Visibility.visible_for_user?(activity, user) do
273 conn
274 |> put_view(ActivityView)
275 |> render("activity.json", %{activity: activity, for: user})
276 end
277 end
278
279 def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
280 with context when is_binary(context) <- TwitterAPI.conversation_id_to_context(id),
281 activities <-
282 ActivityPub.fetch_activities_for_context(context, %{
283 "blocking_user" => user,
284 "user" => user
285 }) do
286 conn
287 |> put_view(ActivityView)
288 |> render("index.json", %{activities: activities, for: user})
289 end
290 end
291
292 @doc """
293 Updates metadata of uploaded media object.
294 Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
295 """
296 def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
297 object = Repo.get(Object, id)
298 description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
299
300 {conn, status, response_body} =
301 cond do
302 !object ->
303 {halt(conn), :not_found, ""}
304
305 !Object.authorize_mutation(object, user) ->
306 {halt(conn), :forbidden, "You can only update your own uploads."}
307
308 !is_binary(description) ->
309 {conn, :not_modified, ""}
310
311 true ->
312 new_data = Map.put(object.data, "name", description)
313
314 {:ok, _} =
315 object
316 |> Object.change(%{data: new_data})
317 |> Repo.update()
318
319 {conn, :no_content, ""}
320 end
321
322 conn
323 |> put_status(status)
324 |> json(response_body)
325 end
326
327 def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
328 response = TwitterAPI.upload(media, user)
329
330 conn
331 |> put_resp_content_type("application/atom+xml")
332 |> send_resp(200, response)
333 end
334
335 def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
336 response = TwitterAPI.upload(media, user, "json")
337
338 conn
339 |> json_reply(200, response)
340 end
341
342 def get_by_id_or_ap_id(id) do
343 activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
344
345 if activity.data["type"] == "Create" do
346 activity
347 else
348 Activity.get_create_by_object_ap_id(activity.data["object"])
349 end
350 end
351
352 def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
353 with {:ok, activity} <- TwitterAPI.fav(user, id) do
354 conn
355 |> put_view(ActivityView)
356 |> render("activity.json", %{activity: activity, for: user})
357 else
358 _ -> json_reply(conn, 400, Jason.encode!(%{}))
359 end
360 end
361
362 def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
363 with {:ok, activity} <- TwitterAPI.unfav(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 retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
373 with {:ok, activity} <- TwitterAPI.repeat(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 unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
383 with {:ok, activity} <- TwitterAPI.unrepeat(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 pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
393 with {:ok, activity} <- TwitterAPI.pin(user, id) do
394 conn
395 |> put_view(ActivityView)
396 |> render("activity.json", %{activity: activity, for: user})
397 else
398 {:error, message} -> bad_request_reply(conn, message)
399 err -> err
400 end
401 end
402
403 def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
404 with {:ok, activity} <- TwitterAPI.unpin(user, id) do
405 conn
406 |> put_view(ActivityView)
407 |> render("activity.json", %{activity: activity, for: user})
408 else
409 {:error, message} -> bad_request_reply(conn, message)
410 err -> err
411 end
412 end
413
414 def register(conn, params) do
415 with {:ok, user} <- TwitterAPI.register_user(params) do
416 conn
417 |> put_view(UserView)
418 |> render("show.json", %{user: user})
419 else
420 {:error, errors} ->
421 conn
422 |> json_reply(400, Jason.encode!(errors))
423 end
424 end
425
426 def password_reset(conn, params) do
427 nickname_or_email = params["email"] || params["nickname"]
428
429 with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
430 json_response(conn, :no_content, "")
431 end
432 end
433
434 def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
435 with %User{} = user <- Repo.get(User, uid),
436 true <- user.local,
437 true <- user.info.confirmation_pending,
438 true <- user.info.confirmation_token == token,
439 info_change <- User.Info.confirmation_changeset(user.info, :confirmed),
440 changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
441 {:ok, _} <- User.update_and_set_cache(changeset) do
442 conn
443 |> redirect(to: "/")
444 end
445 end
446
447 def resend_confirmation_email(conn, params) do
448 nickname_or_email = params["email"] || params["nickname"]
449
450 with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
451 {:ok, _} <- User.try_send_confirmation_email(user) do
452 conn
453 |> json_response(:no_content, "")
454 end
455 end
456
457 def update_avatar(%{assigns: %{user: user}} = conn, params) do
458 {:ok, object} = ActivityPub.upload(params, type: :avatar)
459 change = Changeset.change(user, %{avatar: object.data})
460 {:ok, user} = User.update_and_set_cache(change)
461 CommonAPI.update(user)
462
463 conn
464 |> put_view(UserView)
465 |> render("show.json", %{user: user, for: user})
466 end
467
468 def update_banner(%{assigns: %{user: user}} = conn, params) do
469 with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
470 new_info <- %{"banner" => object.data},
471 info_cng <- User.Info.profile_update(user.info, new_info),
472 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
473 {:ok, user} <- User.update_and_set_cache(changeset) do
474 CommonAPI.update(user)
475 %{"url" => [%{"href" => href} | _]} = object.data
476 response = %{url: href} |> Jason.encode!()
477
478 conn
479 |> json_reply(200, response)
480 end
481 end
482
483 def update_background(%{assigns: %{user: user}} = conn, params) do
484 with {:ok, object} <- ActivityPub.upload(params, type: :background),
485 new_info <- %{"background" => object.data},
486 info_cng <- User.Info.profile_update(user.info, new_info),
487 changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
488 {:ok, _user} <- User.update_and_set_cache(changeset) do
489 %{"url" => [%{"href" => href} | _]} = object.data
490 response = %{url: href} |> Jason.encode!()
491
492 conn
493 |> json_reply(200, response)
494 end
495 end
496
497 def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do
498 with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri),
499 response <- Jason.encode!(user_map) do
500 conn
501 |> json_reply(200, response)
502 else
503 _e ->
504 conn
505 |> put_status(404)
506 |> json(%{error: "Can't find user"})
507 end
508 end
509
510 def followers(%{assigns: %{user: for_user}} = conn, params) do
511 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
512
513 with {:ok, user} <- TwitterAPI.get_user(for_user, params),
514 {:ok, followers} <- User.get_followers(user, page) do
515 followers =
516 cond do
517 for_user && user.id == for_user.id -> followers
518 user.info.hide_followers -> []
519 true -> followers
520 end
521
522 conn
523 |> put_view(UserView)
524 |> render("index.json", %{users: followers, for: conn.assigns[:user]})
525 else
526 _e -> bad_request_reply(conn, "Can't get followers")
527 end
528 end
529
530 def friends(%{assigns: %{user: for_user}} = conn, params) do
531 {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1)
532 {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false)
533
534 page = if export, do: nil, else: page
535
536 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
537 {:ok, friends} <- User.get_friends(user, page) do
538 friends =
539 cond do
540 for_user && user.id == for_user.id -> friends
541 user.info.hide_follows -> []
542 true -> friends
543 end
544
545 conn
546 |> put_view(UserView)
547 |> render("index.json", %{users: friends, for: conn.assigns[:user]})
548 else
549 _e -> bad_request_reply(conn, "Can't get friends")
550 end
551 end
552
553 def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
554 with oauth_tokens <- Token.get_user_tokens(user) do
555 conn
556 |> put_view(TokenView)
557 |> render("index.json", %{tokens: oauth_tokens})
558 end
559 end
560
561 def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
562 Token.delete_user_token(user, id)
563
564 json_reply(conn, 201, "")
565 end
566
567 def blocks(%{assigns: %{user: user}} = conn, _params) do
568 with blocked_users <- User.blocked_users(user) do
569 conn
570 |> put_view(UserView)
571 |> render("index.json", %{users: blocked_users, for: user})
572 end
573 end
574
575 def friend_requests(conn, params) do
576 with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
577 {:ok, friend_requests} <- User.get_follow_requests(user) do
578 conn
579 |> put_view(UserView)
580 |> render("index.json", %{users: friend_requests, for: conn.assigns[:user]})
581 else
582 _e -> bad_request_reply(conn, "Can't get friend requests")
583 end
584 end
585
586 def approve_friend_request(conn, %{"user_id" => uid} = _params) do
587 with followed <- conn.assigns[:user],
588 %User{} = follower <- Repo.get(User, uid),
589 {:ok, follower} <- User.maybe_follow(follower, followed),
590 %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
591 {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
592 {:ok, _activity} <-
593 ActivityPub.accept(%{
594 to: [follower.ap_id],
595 actor: followed,
596 object: follow_activity.data["id"],
597 type: "Accept"
598 }) do
599 conn
600 |> put_view(UserView)
601 |> render("show.json", %{user: follower, for: followed})
602 else
603 e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}")
604 end
605 end
606
607 def deny_friend_request(conn, %{"user_id" => uid} = _params) do
608 with followed <- conn.assigns[:user],
609 %User{} = follower <- Repo.get(User, uid),
610 %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
611 {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
612 {:ok, _activity} <-
613 ActivityPub.reject(%{
614 to: [follower.ap_id],
615 actor: followed,
616 object: follow_activity.data["id"],
617 type: "Reject"
618 }) do
619 conn
620 |> put_view(UserView)
621 |> render("show.json", %{user: follower, for: followed})
622 else
623 e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}")
624 end
625 end
626
627 def friends_ids(%{assigns: %{user: user}} = conn, _params) do
628 with {:ok, friends} <- User.get_friends(user) do
629 ids =
630 friends
631 |> Enum.map(fn x -> x.id end)
632 |> Jason.encode!()
633
634 json(conn, ids)
635 else
636 _e -> bad_request_reply(conn, "Can't get friends")
637 end
638 end
639
640 def empty_array(conn, _params) do
641 json(conn, Jason.encode!([]))
642 end
643
644 def raw_empty_array(conn, _params) do
645 json(conn, [])
646 end
647
648 defp build_info_cng(user, params) do
649 info_params =
650 ["no_rich_text", "locked", "hide_followers", "hide_follows", "show_role"]
651 |> Enum.reduce(%{}, fn key, res ->
652 if value = params[key] do
653 Map.put(res, key, value == "true")
654 else
655 res
656 end
657 end)
658
659 info_params =
660 if value = params["default_scope"] do
661 Map.put(info_params, "default_scope", value)
662 else
663 info_params
664 end
665
666 User.Info.profile_update(user.info, info_params)
667 end
668
669 defp parse_profile_bio(user, params) do
670 if bio = params["description"] do
671 Map.put(params, "bio", User.parse_bio(bio, user))
672 else
673 params
674 end
675 end
676
677 def update_profile(%{assigns: %{user: user}} = conn, params) do
678 params = parse_profile_bio(user, params)
679 info_cng = build_info_cng(user, params)
680
681 with changeset <- User.update_changeset(user, params),
682 changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
683 {:ok, user} <- User.update_and_set_cache(changeset) do
684 CommonAPI.update(user)
685
686 conn
687 |> put_view(UserView)
688 |> render("user.json", %{user: user, for: user})
689 else
690 error ->
691 Logger.debug("Can't update user: #{inspect(error)}")
692 bad_request_reply(conn, "Can't update user")
693 end
694 end
695
696 def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do
697 activities = TwitterAPI.search(user, params)
698
699 conn
700 |> put_view(ActivityView)
701 |> render("index.json", %{activities: activities, for: user})
702 end
703
704 def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
705 users = User.search(query, true, user)
706
707 conn
708 |> put_view(UserView)
709 |> render("index.json", %{users: users, for: user})
710 end
711
712 defp bad_request_reply(conn, error_message) do
713 json = error_json(conn, error_message)
714 json_reply(conn, 400, json)
715 end
716
717 defp json_reply(conn, status, json) do
718 conn
719 |> put_resp_content_type("application/json")
720 |> send_resp(status, json)
721 end
722
723 defp forbidden_json_reply(conn, error_message) do
724 json = error_json(conn, error_message)
725 json_reply(conn, 403, json)
726 end
727
728 def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
729
730 def only_if_public_instance(conn, _) do
731 if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
732 conn
733 else
734 conn
735 |> forbidden_json_reply("Invalid credentials.")
736 |> halt()
737 end
738 end
739
740 defp error_json(conn, error_message) do
741 %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!()
742 end
743
744 def errors(conn, {:param_cast, _}) do
745 conn
746 |> put_status(400)
747 |> json("Invalid parameters")
748 end
749
750 def errors(conn, _) do
751 conn
752 |> put_status(500)
753 |> json("Something went wrong")
754 end
755 end