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