total rebase
[anni] / lib / pleroma / web / mastodon_api / views / instance_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.InstanceView do
6   use Pleroma.Web, :view
7
8   alias Pleroma.Config
9   alias Pleroma.Web.ActivityPub.MRF
10
11   @mastodon_api_level "2.7.2"
12
13   def render("show.json", _) do
14     instance = Config.get(:instance)
15
16     common_information(instance)
17     |> Map.merge(%{
18       uri: Pleroma.Web.WebFinger.host(),
19       description: Keyword.get(instance, :description),
20       short_description: Keyword.get(instance, :short_description),
21       email: Keyword.get(instance, :email),
22       urls: %{
23         streaming_api: Pleroma.Web.Endpoint.websocket_url()
24       },
25       stats: Pleroma.Stats.get_stats(),
26       thumbnail:
27         URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
28         |> to_string,
29       registrations: Keyword.get(instance, :registrations_open),
30       approval_required: Keyword.get(instance, :account_approval_required),
31       contact_account: contact_account(Keyword.get(instance, :contact_username)),
32       configuration: configuration(),
33       # Extra (not present in Mastodon):
34       max_toot_chars: Keyword.get(instance, :limit),
35       max_media_attachments: Keyword.get(instance, :max_media_attachments),
36       poll_limits: Keyword.get(instance, :poll_limits),
37       upload_limit: Keyword.get(instance, :upload_limit),
38       avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
39       background_upload_limit: Keyword.get(instance, :background_upload_limit),
40       banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
41       background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
42       shout_limit: Config.get([:shout, :limit]),
43       description_limit: Keyword.get(instance, :description_limit),
44       chat_limit: Keyword.get(instance, :chat_limit),
45       pleroma: pleroma_configuration(instance)
46     })
47   end
48
49   def render("show2.json", _) do
50     instance = Config.get(:instance)
51
52     common_information(instance)
53     |> Map.merge(%{
54       domain: Pleroma.Web.WebFinger.host(),
55       source_url: Pleroma.Application.repository(),
56       description: Keyword.get(instance, :short_description),
57       usage: %{users: %{active_month: Pleroma.User.active_user_count()}},
58       thumbnail: %{
59         url:
60           URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
61           |> to_string
62       },
63       configuration: configuration2(),
64       registrations: %{
65         enabled: Keyword.get(instance, :registrations_open),
66         approval_required: Keyword.get(instance, :account_approval_required),
67         message: nil,
68         url: nil
69       },
70       contact: %{
71         email: Keyword.get(instance, :email),
72         account: contact_account(Keyword.get(instance, :contact_username))
73       },
74       # Extra (not present in Mastodon):
75       pleroma: pleroma_configuration2(instance)
76     })
77   end
78
79   defp common_information(instance) do
80     %{
81       title: Keyword.get(instance, :name),
82       version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
83       languages: Keyword.get(instance, :languages, ["en"]),
84       rules: []
85     }
86   end
87
88   def features do
89     [
90       "pleroma_api",
91       "mastodon_api",
92       "mastodon_api_streaming",
93       "polls",
94       "v2_suggestions",
95       "pleroma_explicit_addressing",
96       "shareable_emoji_packs",
97       "multifetch",
98       "pleroma:api/v1/notifications:include_types_filter",
99       "editing",
100       "quote_posting",
101       if Config.get([:activitypub, :blockers_visible]) do
102         "blockers_visible"
103       end,
104       if Config.get([:media_proxy, :enabled]) do
105         "media_proxy"
106       end,
107       if Config.get([:gopher, :enabled]) do
108         "gopher"
109       end,
110       # backwards compat
111       if Config.get([:shout, :enabled]) do
112         "chat"
113       end,
114       if Config.get([:shout, :enabled]) do
115         "shout"
116       end,
117       if Config.get([:instance, :allow_relay]) do
118         "relay"
119       end,
120       if Config.get([:instance, :safe_dm_mentions]) do
121         "safe_dm_mentions"
122       end,
123       "pleroma_emoji_reactions",
124       "pleroma_custom_emoji_reactions",
125       "pleroma_chat_messages",
126       if Config.get([:instance, :show_reactions]) do
127         "exposable_reactions"
128       end,
129       if Config.get([:instance, :profile_directory]) do
130         "profile_directory"
131       end,
132       "pleroma:get:main/ostatus",
133       "pleroma:group_actors",
134       "pleroma:bookmark_folders"
135     ]
136     |> Enum.filter(& &1)
137   end
138
139   def federation do
140     quarantined = Config.get([:instance, :quarantined_instances], [])
141
142     if Config.get([:mrf, :transparency]) do
143       {:ok, data} = MRF.describe()
144
145       data
146       |> Map.put(
147         :quarantined_instances,
148         Enum.map(quarantined, fn {instance, _reason} -> instance end)
149       )
150       # This is for backwards compatibility. We originally didn't sent
151       # extra info like a reason why an instance was rejected/quarantined/etc.
152       # Because we didn't want to break backwards compatibility it was decided
153       # to add an extra "info" key.
154       |> Map.put(:quarantined_instances_info, %{
155         "quarantined_instances" =>
156           quarantined
157           |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
158           |> Map.new()
159       })
160     else
161       %{}
162     end
163     |> Map.put(:enabled, Config.get([:instance, :federating]))
164   end
165
166   defp fields_limits do
167     %{
168       max_fields: Config.get([:instance, :max_account_fields]),
169       max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
170       name_length: Config.get([:instance, :account_field_name_length]),
171       value_length: Config.get([:instance, :account_field_value_length])
172     }
173   end
174
175   defp contact_account(nil), do: nil
176
177   defp contact_account("@" <> username) do
178     contact_account(username)
179   end
180
181   defp contact_account(username) do
182     user = Pleroma.User.get_cached_by_nickname(username)
183
184     if user do
185       Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user, for: nil})
186     else
187       nil
188     end
189   end
190
191   defp configuration do
192     %{
193       accounts: %{
194         max_featured_tags: 0
195       },
196       statuses: %{
197         max_characters: Config.get([:instance, :limit]),
198         max_media_attachments: Config.get([:instance, :max_media_attachments])
199       },
200       media_attachments: %{
201         image_size_limit: Config.get([:instance, :upload_limit]),
202         video_size_limit: Config.get([:instance, :upload_limit]),
203         supported_mime_types: ["application/octet-stream"]
204       },
205       polls: %{
206         max_options: Config.get([:instance, :poll_limits, :max_options]),
207         max_characters_per_option: Config.get([:instance, :poll_limits, :max_option_chars]),
208         min_expiration: Config.get([:instance, :poll_limits, :min_expiration]),
209         max_expiration: Config.get([:instance, :poll_limits, :max_expiration])
210       }
211     }
212   end
213
214   defp configuration2 do
215     configuration()
216     |> put_in([:accounts, :max_pinned_statuses], Config.get([:instance, :max_pinned_statuses], 0))
217     |> put_in([:statuses, :characters_reserved_per_url], 0)
218     |> Map.merge(%{
219       urls: %{
220         streaming: Pleroma.Web.Endpoint.websocket_url(),
221         status: Config.get([:instance, :status_page])
222       },
223       vapid: %{
224         public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
225       }
226     })
227   end
228
229   defp pleroma_configuration(instance) do
230     %{
231       metadata: %{
232         account_activation_required: Keyword.get(instance, :account_activation_required),
233         features: features(),
234         federation: federation(),
235         fields_limits: fields_limits(),
236         post_formats: Config.get([:instance, :allowed_post_formats]),
237         birthday_required: Config.get([:instance, :birthday_required]),
238         birthday_min_age: Config.get([:instance, :birthday_min_age])
239       },
240       stats: %{mau: Pleroma.User.active_user_count()},
241       vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
242     }
243   end
244
245   defp pleroma_configuration2(instance) do
246     configuration = pleroma_configuration(instance)
247
248     configuration
249     |> Map.merge(%{
250       metadata:
251         configuration.metadata
252         |> Map.merge(%{
253           avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
254           background_upload_limit: Keyword.get(instance, :background_upload_limit),
255           banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
256           background_image:
257             Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
258           chat_limit: Keyword.get(instance, :chat_limit),
259           description_limit: Keyword.get(instance, :description_limit),
260           shout_limit: Config.get([:shout, :limit])
261         })
262     })
263   end
264 end