X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fmastodon_api%2Fmastodon_api_controller.ex;h=9d4e3d80625dc51a9374b4d08dfdce51c5c9e94d;hb=4856ba596f5682a48a5cd17cacb607d996764f7a;hp=b00f1e15c1a58506e02f597fb4b821f7c9040410;hpb=d7c2909ce2e488e049a04ba6ba2a79d392d60bde;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 b00f1e15c..9d4e3d806 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -2,7 +2,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller alias Pleroma.{Repo, Activity, User, Notification, Stats} alias Pleroma.Web - alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView} + alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.{CommonAPI, OStatus} alias Pleroma.Web.OAuth.{Authorization, Token, App} @@ -10,6 +10,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do import Ecto.Query require Logger + action_fallback(:errors) + def create_app(conn, params) do with cs <- App.register_changeset(%App{}, params) |> IO.inspect(), {:ok, app} <- Repo.insert(cs) |> IO.inspect() do @@ -112,7 +114,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})", email: Keyword.get(@instance, :email), urls: %{ - streaming_api: String.replace(Web.base_url(), ["http", "https"], "wss") + streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws") }, stats: Stats.get_stats(), thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg", @@ -144,7 +146,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, mastodon_emoji) end - defp add_link_headers(conn, method, activities, param \\ false) do + defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do last = List.last(activities) first = List.first(activities) @@ -155,13 +157,31 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do {next_url, prev_url} = if param do { - mastodon_api_url(Pleroma.Web.Endpoint, method, param, max_id: min), - mastodon_api_url(Pleroma.Web.Endpoint, method, param, since_id: max) + mastodon_api_url( + Pleroma.Web.Endpoint, + method, + param, + Map.merge(params, %{max_id: min}) + ), + mastodon_api_url( + Pleroma.Web.Endpoint, + method, + param, + Map.merge(params, %{since_id: max}) + ) } else { - mastodon_api_url(Pleroma.Web.Endpoint, method, max_id: min), - mastodon_api_url(Pleroma.Web.Endpoint, method, since_id: max) + mastodon_api_url( + Pleroma.Web.Endpoint, + method, + Map.merge(params, %{max_id: min}) + ), + mastodon_api_url( + Pleroma.Web.Endpoint, + method, + Map.merge(params, %{since_id: max}) + ) } end @@ -189,10 +209,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def public_timeline(%{assigns: %{user: user}} = conn, params) do + local_only = params["local"] in [true, "True", "true", "1"] + params = params |> Map.put("type", ["Create", "Announce"]) - |> Map.put("local_only", params["local"] in [true, "True", "true", "1"]) + |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) activities = @@ -200,28 +222,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> Enum.reverse() conn - |> add_link_headers(:public_timeline, activities) + |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end - def user_statuses(%{assigns: %{user: user}} = conn, params) do - with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do - params = - params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("actor_id", ap_id) - |> Map.put("whole_db", true) - + def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do + with %User{} = user <- Repo.get(User, params["id"]) do + # Since Pleroma has no "pinned" posts feature, we'll just set an empty list here activities = - ActivityPub.fetch_public_activities(params) - |> Enum.reverse() + if params["pinned"] == "true" do + [] + else + ActivityPub.fetch_user_activities(user, reading_user, params) + end conn |> add_link_headers(:user_statuses, activities, params["id"]) - |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) + |> render(StatusView, "index.json", %{ + activities: activities, + for: reading_user, + as: :activity + }) end end + def dm_timeline(%{assigns: %{user: user}} = conn, params) do + query = + ActivityPub.fetch_activities_query([user.ap_id], %{"type" => "Create", visibility: "direct"}) + + activities = Repo.all(query) + + conn + |> add_link_headers(:dm_timeline, activities) + |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) + 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 @@ -270,7 +305,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> Map.put("in_reply_to_status_id", params["in_reply_to_id"]) |> Map.put("no_attachment_links", true) - {:ok, activity} = CommonAPI.post(user, params) + idempotency_key = + case get_req_header(conn, "idempotency-key") do + [key] -> key + _ -> Ecto.UUID.generate() + end + + {:ok, activity} = + Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end) + render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end @@ -286,20 +329,27 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do - with {:ok, announce, _activity} = CommonAPI.repeat(ap_id_or_id, user) do + with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user) do render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity}) end end + def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do + with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), + %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do + render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) + end + end + def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do - with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user), + with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end end def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do - with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user), + with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end @@ -384,10 +434,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do + local_only = params["local"] in [true, "True", "true", "1"] + params = params |> Map.put("type", "Create") - |> Map.put("local_only", !!params["local"]) + |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) activities = @@ -395,7 +447,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> Enum.reverse() conn - |> add_link_headers(:hashtag_timeline, activities, params["tag"]) + |> add_link_headers(:hashtag_timeline, activities, params["tag"], %{"local" => local_only}) |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end @@ -416,7 +468,7 @@ 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.follow(follower, followed), + {:ok, follower} <- User.maybe_direct_follow(follower, followed), {:ok, _activity} <- ActivityPub.follow(follower, followed) do render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) else @@ -429,7 +481,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.follow(follower, followed), + {:ok, follower} <- User.maybe_direct_follow(follower, followed), {:ok, _activity} <- ActivityPub.follow(follower, followed) do render(conn, AccountView, "account.json", %{user: followed}) else @@ -440,24 +492,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - # TODO: Clean up and unify def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), - {:ok, follower, follow_activity} <- User.unfollow(follower, followed), - {:ok, _activity} <- - ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - # get latest Follow for these users - "object" => follow_activity.data["id"] - }) do + {:ok, _activity} <- ActivityPub.unfollow(follower, followed), + {:ok, follower, _} <- User.unfollow(follower, followed) do render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) end end def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), - {:ok, blocker} <- User.block(blocker, blocked) do + {:ok, blocker} <- User.block(blocker, blocked), + {:ok, _activity} <- ActivityPub.block(blocker, blocked) do render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> @@ -469,7 +515,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), - {:ok, blocker} <- User.unblock(blocker, blocked) do + {:ok, blocker} <- User.unblock(blocker, blocked), + {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> @@ -515,7 +562,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do a.data, ^query ), - limit: 20 + limit: 20, + order_by: [desc: :id] ) statuses = Repo.all(q) ++ fetched @@ -559,6 +607,102 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end + def get_lists(%{assigns: %{user: user}} = conn, opts) do + lists = Pleroma.List.for_user(user, opts) + res = ListView.render("lists.json", lists: lists) + json(conn, res) + end + + def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(id, user) do + res = ListView.render("list.json", list: list) + json(conn, res) + else + _e -> json(conn, "error") + end + end + + def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), + {:ok, _list} <- Pleroma.List.delete(list) do + json(conn, %{}) + else + _e -> + json(conn, "error") + end + end + + def create_list(%{assigns: %{user: user}} = conn, %{"title" => title}) do + with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do + res = ListView.render("list.json", list: list) + json(conn, res) + end + end + + def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do + accounts + |> Enum.each(fn account_id -> + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), + %User{} = followed <- Repo.get(User, account_id) do + Pleroma.List.follow(list, followed) + end + end) + + json(conn, %{}) + end + + def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do + accounts + |> Enum.each(fn account_id -> + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), + %User{} = followed <- Repo.get(Pleroma.User, account_id) do + Pleroma.List.unfollow(list, followed) + end + end) + + json(conn, %{}) + end + + def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), + {:ok, users} = Pleroma.List.get_following(list) do + render(conn, AccountView, "accounts.json", %{users: users, as: :user}) + end + end + + def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title}) do + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), + {:ok, list} <- Pleroma.List.rename(list, title) do + res = ListView.render("list.json", list: list) + json(conn, res) + else + _e -> + json(conn, "error") + end + end + + def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do + with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(id, user) do + params = + params + |> Map.put("type", "Create") + |> Map.put("blocking_user", user) + + # adding title is a hack to not make empty lists function like a public timeline + activities = + ActivityPub.fetch_activities([title | following], params) + |> Enum.reverse() + + conn + |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) + else + _e -> + conn + |> put_status(403) + |> json(%{error: "Error."}) + end + end + def index(%{assigns: %{user: user}} = conn, _params) do token = conn @@ -605,35 +749,37 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do "video\/mp4" ] }, - settings: Map.get(user.info, "settings") || %{ - onboarded: true, - home: %{ - shows: %{ - reblog: true, - reply: true - } - }, - notifications: %{ - alerts: %{ - follow: true, - favourite: true, - reblog: true, - mention: true - }, - shows: %{ - follow: true, - favourite: true, - reblog: true, - mention: true + settings: + Map.get(user.info, "settings") || + %{ + onboarded: true, + home: %{ + shows: %{ + reblog: true, + reply: true + } + }, + notifications: %{ + alerts: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + }, + shows: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + }, + sounds: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + } + } }, - sounds: %{ - follow: true, - favourite: true, - reblog: true, - mention: true - } - } - }, push_subscription: nil, accounts: accounts, custom_emojis: mastodon_emoji, @@ -656,7 +802,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do {:ok, _user} <- User.update_and_set_cache(change) do conn |> json(%{}) - else e -> + else + e -> conn |> json(%{error: inspect(e)}) end @@ -684,7 +831,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def login_post(conn, %{"authorization" => %{"name" => name, "password" => password}}) do - with %User{} = user <- User.get_cached_by_nickname(name), + with %User{} = user <- User.get_by_nickname_or_email(name), true <- Pbkdf2.checkpw(password, user.password_hash), {:ok, app} <- get_or_make_app(), {:ok, auth} <- Authorization.create_authorization(app, user), @@ -774,4 +921,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do nil end end + + def errors(conn, _) do + conn + |> put_status(500) + |> json("Something went wrong") + end end