X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fmastodon_api%2Fmastodon_api_controller.ex;h=18e4ddb88fcbd824d46716db8b3e2c8740964e32;hb=4212527928020de5b67424f090c67fc20d0844af;hp=3a343f3d8d209c24fb9c5fcccfbba69efc96e6a2;hpb=5a46d37af9fb1914d795cb90d28356efcd0790d5;p=akkoma diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 3a343f3d8..18e4ddb88 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller + alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Filter @@ -13,24 +14,26 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Stats alias Pleroma.User alias Pleroma.Web + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MediaProxy - alias Pleroma.Web.Push - alias Push.Subscription - alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.AppView alias Pleroma.Web.MastodonAPI.FilterView alias Pleroma.Web.MastodonAPI.ListView + alias Pleroma.Web.MastodonAPI.MastodonAPI alias Pleroma.Web.MastodonAPI.MastodonView - alias Pleroma.Web.MastodonAPI.PushSubscriptionView + alias Pleroma.Web.MastodonAPI.NotificationView + alias Pleroma.Web.MastodonAPI.ReportView alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.MediaProxy alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Token + import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2] import Ecto.Query + require Logger @httpoison Application.get_env(:pleroma, :httpoison) @@ -39,19 +42,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do action_fallback(:errors) def create_app(conn, params) do - with cs <- App.register_changeset(%App{}, params), + scopes = oauth_scopes(params, ["read"]) + + app_attrs = + params + |> Map.drop(["scope", "scopes"]) + |> Map.put("scopes", scopes) + + with cs <- App.register_changeset(%App{}, app_attrs), false <- cs.changes[:client_name] == @local_mastodon_name, {:ok, app} <- Repo.insert(cs) do - res = %{ - id: app.id |> to_string, - name: app.client_name, - client_id: app.client_id, - client_secret: app.client_secret, - redirect_uri: app.redirect_uris, - website: app.website - } - - json(conn, res) + conn + |> put_view(AppView) + |> render("show.json", %{app: app}) end end @@ -123,8 +126,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, account) end - def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do - with %User{} = user <- Repo.get(User, id), + def verify_app_credentials(%{assigns: %{user: _user, token: token}} = conn, _) do + with %Token{app: %App{} = app} <- Repo.preload(token, :app) do + conn + |> put_view(AppView) + |> render("short.json", %{app: app}) + end + end + + def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id), true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do account = AccountView.render("account.json", %{user: user, for: for_user}) json(conn, account) @@ -152,6 +163,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do }, stats: Stats.get_stats(), thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg", + languages: ["en"], + registrations: Pleroma.Config.get([:instance, :registrations_open]), + # Extra (not present in Mastodon): max_toot_chars: Keyword.get(instance, :limit) } @@ -182,6 +196,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do + params = + conn.params + |> Map.drop(["since_id", "max_id"]) + |> Map.merge(params) + last = List.last(activities) first = List.first(activities) @@ -281,13 +300,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def dm_timeline(%{assigns: %{user: user}} = conn, params) do - query = - ActivityPub.fetch_activities_query( - [user.ap_id], - Map.merge(params, %{"type" => "Create", visibility: "direct"}) - ) + params = + params + |> Map.put("type", "Create") + |> Map.put("blocking_user", user) + |> Map.put("user", user) + |> Map.put(:visibility, "direct") - activities = Repo.all(query) + activities = + [user.ap_id] + |> ActivityPub.fetch_activities_query(params) + |> Repo.all() conn |> add_link_headers(:dm_timeline, activities) @@ -296,8 +319,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Activity{} = activity <- Repo.get(Activity, id), - true <- ActivityPub.visible_for_user?(activity, user) do + with %Activity{} = activity <- Activity.get_by_id(id), + true <- Visibility.visible_for_user?(activity, user) do conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user}) @@ -305,7 +328,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Activity{} = activity <- Repo.get(Activity, id), + with %Activity{} = activity <- Activity.get_by_id(id), activities <- ActivityPub.fetch_activities_for_context(activity.data["context"], %{ "blocking_user" => user, @@ -437,9 +460,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Activity{} = activity <- Repo.get(Activity, id), + with %Activity{} = activity <- Activity.get_by_id(id), %User{} = user <- User.get_by_nickname(user.nickname), - true <- ActivityPub.visible_for_user?(activity, user), + true <- Visibility.visible_for_user?(activity, user), {:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do conn |> put_view(StatusView) @@ -448,9 +471,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Activity{} = activity <- Repo.get(Activity, id), + with %Activity{} = activity <- Activity.get_by_id(id), %User{} = user <- User.get_by_nickname(user.nickname), - true <- ActivityPub.visible_for_user?(activity, user), + true <- Visibility.visible_for_user?(activity, user), {:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do conn |> put_view(StatusView) @@ -484,21 +507,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def notifications(%{assigns: %{user: user}} = conn, params) do - notifications = Notification.for_user(user, params) - - result = - notifications - |> Enum.map(fn x -> render_notification(user, x) end) - |> Enum.filter(& &1) + notifications = MastodonAPI.get_notifications(user, params) conn |> add_link_headers(:notifications, notifications) - |> json(result) + |> put_view(NotificationView) + |> render("index.json", %{notifications: notifications, for: user}) end def get_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do with {:ok, notification} <- Notification.get(user, id) do - json(conn, render_notification(user, notification)) + conn + |> put_view(NotificationView) + |> render("show.json", %{notification: notification, for: user}) else {:error, reason} -> conn @@ -572,7 +593,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def favourited_by(conn, %{"id" => id}) do - with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do + with %Activity{data: %{"object" => %{"likes" => likes}}} <- Activity.get_by_id(id) do q = from(u in User, where: u.ap_id in ^likes) users = Repo.all(q) @@ -585,7 +606,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def reblogged_by(conn, %{"id" => id}) do - with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do + with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Activity.get_by_id(id) do q = from(u in User, where: u.ap_id in ^announces) users = Repo.all(q) @@ -635,9 +656,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> render("index.json", %{activities: activities, for: user, as: :activity}) end - def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do + def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do with %User{} = user <- Repo.get(User, id), - {:ok, followers} <- User.get_followers(user) do + followers <- MastodonAPI.get_followers(user, params) do followers = cond do for_user && user.id == for_user.id -> followers @@ -646,14 +667,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end conn + |> add_link_headers(:followers, followers, user) |> put_view(AccountView) |> render("accounts.json", %{users: followers, as: :user}) end end - def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do + def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do with %User{} = user <- Repo.get(User, id), - {:ok, followers} <- User.get_friends(user) do + followers <- MastodonAPI.get_friends(user, params) do followers = cond do for_user && user.id == for_user.id -> followers @@ -662,6 +684,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end conn + |> add_link_headers(:following, followers, user) |> put_view(AccountView) |> render("accounts.json", %{users: followers, as: :user}) end @@ -677,16 +700,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do with %User{} = follower <- Repo.get(User, id), - {:ok, follower} <- User.maybe_follow(follower, followed), - %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), - {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"), - {:ok, _activity} <- - ActivityPub.accept(%{ - to: [follower.ap_id], - actor: followed, - object: follow_activity.data["id"], - type: "Accept" - }) do + {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do conn |> put_view(AccountView) |> render("relationship.json", %{user: followed, target: follower}) @@ -700,15 +714,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do with %User{} = follower <- Repo.get(User, id), - %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), - {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"), - {:ok, _activity} <- - ActivityPub.reject(%{ - to: [follower.ap_id], - actor: followed, - object: follow_activity.data["id"], - type: "Reject" - }) do + {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do conn |> put_view(AccountView) |> render("relationship.json", %{user: followed, target: follower}) @@ -722,18 +728,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), - {:ok, follower} <- User.maybe_direct_follow(follower, followed), - {:ok, _activity} <- ActivityPub.follow(follower, followed), - {:ok, follower, followed} <- - User.wait_and_refresh( - Config.get([:activitypub, :follow_handshake_timeout]), - follower, - followed - ) do + false <- User.following?(follower, followed), + {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do conn |> put_view(AccountView) |> render("relationship.json", %{user: follower, target: followed}) else + true -> + followed = User.get_cached_by_id(id) + + {:ok, follower} = + case conn.params["reblogs"] do + true -> CommonAPI.show_reblogs(follower, followed) + false -> CommonAPI.hide_reblogs(follower, followed) + end + + conn + |> put_view(AccountView) + |> render("relationship.json", %{user: follower, target: followed}) + {:error, message} -> conn |> put_resp_content_type("application/json") @@ -743,8 +756,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do with %User{} = followed <- Repo.get_by(User, nickname: uri), - {:ok, follower} <- User.maybe_direct_follow(follower, followed), - {:ok, _activity} <- ActivityPub.follow(follower, followed) do + {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do conn |> put_view(AccountView) |> render("account.json", %{user: followed, for: follower}) @@ -758,8 +770,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), - {:ok, _activity} <- ActivityPub.unfollow(follower, followed), - {:ok, follower, _} <- User.unfollow(follower, followed) do + {:ok, follower} <- CommonAPI.unfollow(follower, followed) do conn |> put_view(AccountView) |> render("relationship.json", %{user: follower, target: followed}) @@ -857,7 +868,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do if Regex.match?(~r/https?:/, query) do with {:ok, object} <- ActivityPub.fetch_object_from_id(query), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), - true <- ActivityPub.visible_for_user?(activity, user) do + true <- Visibility.visible_for_user?(activity, user) do [activity] else _e -> [] @@ -883,7 +894,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) statuses = status_search(user, query) @@ -908,7 +919,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) statuses = status_search(user, query) @@ -930,7 +941,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) @@ -938,12 +949,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def favourites(%{assigns: %{user: user}} = conn, params) do - activities = + params = params |> Map.put("type", "Create") |> Map.put("favorited_by", user.ap_id) |> Map.put("blocking_user", user) - |> ActivityPub.fetch_public_activities() + + activities = + ActivityPub.fetch_activities([], params) |> Enum.reverse() conn @@ -1117,7 +1130,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do compose: %{ me: "#{user.id}", default_privacy: user.info.default_scope, - default_sensitive: false + default_sensitive: false, + allow_content_types: Config.get([:instance, :allowed_post_formats]) }, media_attachments: %{ accept_content_types: [ @@ -1254,7 +1268,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do response_type: "code", client_id: app.client_id, redirect_uri: ".", - scope: app.scopes + scope: Enum.join(app.scopes, " ") ) conn @@ -1262,14 +1276,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - defp get_or_make_app() do + defp get_or_make_app do find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."} + scopes = ["read", "write", "follow", "push"] with %App{} = app <- Repo.get_by(App, find_attrs) do + {:ok, app} = + if app.scopes == scopes do + {:ok, app} + else + app + |> Ecto.Changeset.change(%{scopes: scopes}) + |> Repo.update() + end + {:ok, app} else _e -> - cs = App.register_changeset(%App{}, Map.put(find_attrs, :scopes, "read,write,follow")) + cs = + App.register_changeset( + %App{}, + Map.put(find_attrs, :scopes, scopes) + ) Repo.insert(cs) end @@ -1301,45 +1329,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, %{}) end - def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) do - actor = User.get_cached_by_ap_id(activity.data["actor"]) - parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) - mastodon_type = Activity.mastodon_notification_type(activity) - - response = %{ - id: to_string(id), - type: mastodon_type, - created_at: CommonAPI.Utils.to_masto_date(created_at), - account: AccountView.render("account.json", %{user: actor, for: user}) - } - - case mastodon_type do - "mention" -> - response - |> Map.merge(%{ - status: StatusView.render("status.json", %{activity: activity, for: user}) - }) - - "favourite" -> - response - |> Map.merge(%{ - status: StatusView.render("status.json", %{activity: parent_activity, for: user}) - }) - - "reblog" -> - response - |> Map.merge(%{ - status: StatusView.render("status.json", %{activity: parent_activity, for: user}) - }) - - "follow" -> - response - - _ -> - nil - end - end - def get_filters(%{assigns: %{user: user}} = conn, _) do filters = Filter.get_filters(user) res = FilterView.render("filters.json", filters: filters) @@ -1399,37 +1388,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, %{}) end - def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do - true = Push.enabled() - Subscription.delete_if_exists(user, token) - {:ok, subscription} = Subscription.create(user, token, params) - view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) - json(conn, view) - end - - def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do - true = Push.enabled() - subscription = Subscription.get(user, token) - view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) - json(conn, view) - end - - def update_push_subscription( - %{assigns: %{user: user, token: token}} = conn, - params - ) do - true = Push.enabled() - {:ok, subscription} = Subscription.update(user, token, params) - view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) - json(conn, view) - end - - def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do - true = Push.enabled() - {:ok, _response} = Subscription.delete(user, token) - json(conn, %{}) - end - + # fallback action + # def errors(conn, _) do conn |> put_status(500) @@ -1458,7 +1418,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do url, [], adapter: [ - timeout: timeout, recv_timeout: timeout, pool: :default ] @@ -1494,9 +1453,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - def status_card(conn, %{"id" => status_id}) do - with %Activity{} = activity <- Repo.get(Activity, status_id), - true <- ActivityPub.is_public?(activity) do + def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do + with %Activity{} = activity <- Activity.get_by_id(status_id), + true <- Visibility.visible_for_user?(activity, user) do data = StatusView.render( "card.json", @@ -1510,6 +1469,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def reports(%{assigns: %{user: user}} = conn, params) do + case CommonAPI.report(user, params) do + {:ok, activity} -> + conn + |> put_view(ReportView) + |> try_render("report.json", %{activity: activity}) + + {:error, err} -> + conn + |> put_status(:bad_request) + |> json(%{error: err}) + end + end + def try_render(conn, target, params) when is_binary(target) do res = render(conn, target, params)