1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
6 use Pleroma.Web, :controller
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
10 alias Pleroma.Bookmark
12 alias Pleroma.Pagination
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.MastodonView
20 alias Pleroma.Web.MastodonAPI.StatusView
24 action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
26 @mastodon_api_level "2.7.2"
28 def masto_instance(conn, _params) do
29 instance = Config.get(:instance)
33 title: Keyword.get(instance, :name),
34 description: Keyword.get(instance, :description),
35 version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
36 email: Keyword.get(instance, :email),
38 streaming_api: Pleroma.Web.Endpoint.websocket_url()
40 stats: Stats.get_stats(),
41 thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
43 registrations: Pleroma.Config.get([:instance, :registrations_open]),
44 # Extra (not present in Mastodon):
45 max_toot_chars: Keyword.get(instance, :limit),
46 poll_limits: Keyword.get(instance, :poll_limits),
47 upload_limit: Keyword.get(instance, :upload_limit),
48 avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
49 background_upload_limit: Keyword.get(instance, :background_upload_limit),
50 banner_upload_limit: Keyword.get(instance, :banner_upload_limit)
56 def peers(conn, _params) do
57 json(conn, Stats.get_peers())
60 defp mastodonized_emoji do
61 Pleroma.Emoji.get_all()
62 |> Enum.map(fn {shortcode, %Pleroma.Emoji{file: relative_url, tags: tags}} ->
63 url = to_string(URI.merge(Web.base_url(), relative_url))
66 "shortcode" => shortcode,
68 "visible_in_picker" => true,
71 # Assuming that a comma is authorized in the category name
72 "category" => (tags -- ["Custom"]) |> Enum.join(",")
77 def custom_emojis(conn, _params) do
78 mastodon_emoji = mastodonized_emoji()
79 json(conn, mastodon_emoji)
82 def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
83 with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
84 {_, true} <- {:followed, follower.id != followed.id},
85 {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
87 |> put_view(AccountView)
88 |> render("show.json", %{user: followed, for: follower})
95 |> put_status(:forbidden)
96 |> json(%{error: message})
100 def mutes(%{assigns: %{user: user}} = conn, _) do
101 with muted_accounts <- User.muted_users(user) do
102 res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user)
107 def blocks(%{assigns: %{user: user}} = conn, _) do
108 with blocked_accounts <- User.blocked_users(user) do
109 res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user)
114 def favourites(%{assigns: %{user: user}} = conn, params) do
117 |> Map.put("type", "Create")
118 |> Map.put("favorited_by", user.ap_id)
119 |> Map.put("blocking_user", user)
122 ActivityPub.fetch_activities([], params)
126 |> add_link_headers(activities)
127 |> put_view(StatusView)
128 |> render("index.json", %{activities: activities, for: user, as: :activity})
131 def bookmarks(%{assigns: %{user: user}} = conn, params) do
132 user = User.get_cached_by_id(user.id)
135 Bookmark.for_user_query(user.id)
136 |> Pagination.fetch_paginated(params)
140 |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
143 |> add_link_headers(bookmarks)
144 |> put_view(StatusView)
145 |> render("index.json", %{activities: activities, for: user, as: :activity})
148 def index(%{assigns: %{user: user}} = conn, _params) do
149 token = get_session(conn, :oauth_token)
152 mastodon_emoji = mastodonized_emoji()
154 limit = Config.get([:instance, :limit])
156 accounts = Map.put(%{}, user.id, AccountView.render("show.json", %{user: user, for: user}))
161 streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(),
164 domain: Pleroma.Web.Endpoint.host(),
167 unfollow_modal: false,
170 auto_play_gif: false,
171 display_sensitive_media: false,
172 reduce_motion: false,
173 max_toot_chars: limit,
174 mascot: User.get_mascot(user)["url"]
176 poll_limits: Config.get([:instance, :poll_limits]),
178 delete_others_notice: present?(user.info.is_moderator),
179 admin: present?(user.info.is_admin)
183 default_privacy: user.info.default_scope,
184 default_sensitive: false,
185 allow_content_types: Config.get([:instance, :allowed_post_formats])
187 media_attachments: %{
188 accept_content_types: [
204 user.info.settings ||
234 push_subscription: nil,
236 custom_emojis: mastodon_emoji,
243 |> put_view(MastodonView)
244 |> render("index.html", %{initial_state: initial_state})
247 |> put_session(:return_to, conn.request_path)
248 |> redirect(to: "/web/login")
252 def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
253 with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
258 |> put_status(:internal_server_error)
259 |> json(%{error: inspect(e)})
263 # Stubs for unimplemented mastodon api
265 def empty_array(conn, _) do
266 Logger.debug("Unimplemented, returning an empty array")
270 def empty_object(conn, _) do
271 Logger.debug("Unimplemented, returning an empty object")
275 defp present?(nil), do: false
276 defp present?(false), do: false
277 defp present?(_), do: true