Merge branch 'instance-v2' into 'develop'

Implement api/v2/instance route

See merge request pleroma/pleroma!3939
This commit is contained in:
Haelwenn 2023-12-21 21:14:46 +00:00
commit bd50892c25
7 changed files with 296 additions and 23 deletions

View File

@ -0,0 +1 @@
Implement /api/v2/instance route

View File

@ -23,6 +23,18 @@ def show_operation do
} }
end end
def show2_operation do
%Operation{
tags: ["Instance misc"],
summary: "Retrieve instance information",
description: "Information about the server",
operationId: "InstanceController.show2",
responses: %{
200 => Operation.response("Instance", "application/json", instance2())
}
}
end
def peers_operation do def peers_operation do
%Operation{ %Operation{
tags: ["Instance misc"], tags: ["Instance misc"],
@ -165,6 +177,166 @@ defp instance do
} }
end end
defp instance2 do
%Schema{
type: :object,
properties: %{
domain: %Schema{type: :string, description: "The domain name of the instance"},
title: %Schema{type: :string, description: "The title of the website"},
version: %Schema{
type: :string,
description: "The version of Pleroma installed on the instance"
},
source_url: %Schema{
type: :string,
description: "The version of Pleroma installed on the instance"
},
description: %Schema{
type: :string,
description: "Admin-defined description of the Pleroma site"
},
usage: %Schema{
type: :object,
description: "Instance usage statistics",
properties: %{
users: %Schema{
type: :object,
description: "User count statistics",
properties: %{
active_month: %Schema{
type: :integer,
description: "Monthly active users"
}
}
}
}
},
email: %Schema{
type: :string,
description: "An email that may be contacted for any inquiries",
format: :email
},
urls: %Schema{
type: :object,
description: "URLs of interest for clients apps",
properties: %{}
},
stats: %Schema{
type: :object,
description: "Statistics about how much information the instance contains",
properties: %{
user_count: %Schema{
type: :integer,
description: "Users registered on this instance"
},
status_count: %Schema{
type: :integer,
description: "Statuses authored by users on instance"
},
domain_count: %Schema{
type: :integer,
description: "Domains federated with this instance"
}
}
},
thumbnail: %Schema{
type: :object,
properties: %{
url: %Schema{
type: :string,
description: "Banner image for the website",
nullable: true
}
}
},
languages: %Schema{
type: :array,
items: %Schema{type: :string},
description: "Primary langauges of the website and its staff"
},
registrations: %Schema{
type: :object,
description: "Registrations-related configuration",
properties: %{
enabled: %Schema{
type: :boolean,
description: "Whether registrations are enabled"
},
approval_required: %Schema{
type: :boolean,
description: "Whether users need to be manually approved by admin"
}
}
},
configuration: %Schema{
type: :object,
description: "Instance configuration",
properties: %{
urls: %Schema{
type: :object,
properties: %{
streaming: %Schema{
type: :string,
description: "Websockets address for push streaming"
}
}
},
statuses: %Schema{
type: :object,
description: "A map with poll limits for local statuses",
properties: %{
max_characters: %Schema{
type: :integer,
description: "Posts character limit (CW/Subject included in the counter)"
},
max_media_attachments: %Schema{
type: :integer,
description: "Media attachment limit"
}
}
},
media_attachments: %Schema{
type: :object,
description: "A map with poll limits for media attachments",
properties: %{
image_size_limit: %Schema{
type: :integer,
description: "File size limit of uploaded images"
},
video_size_limit: %Schema{
type: :integer,
description: "File size limit of uploaded videos"
}
}
},
polls: %Schema{
type: :object,
description: "A map with poll limits for local polls",
properties: %{
max_options: %Schema{
type: :integer,
description: "Maximum number of options."
},
max_characters_per_option: %Schema{
type: :integer,
description: "Maximum number of characters per option."
},
min_expiration: %Schema{
type: :integer,
description: "Minimum expiration time (in seconds)."
},
max_expiration: %Schema{
type: :integer,
description: "Maximum expiration time (in seconds)."
}
}
}
}
}
}
}
end
defp array_of_domains do defp array_of_domains do
%Schema{ %Schema{
type: :array, type: :array,

View File

@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:skip_auth when action in [:show, :peers]) plug(:skip_auth when action in [:show, :show2, :peers])
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
@ -16,6 +16,11 @@ def show(conn, _params) do
render(conn, "show.json") render(conn, "show.json")
end end
@doc "GET /api/v2/instance"
def show2(conn, _params) do
render(conn, "show2.json")
end
@doc "GET /api/v1/instance/peers" @doc "GET /api/v1/instance/peers"
def peers(conn, _params) do def peers(conn, _params) do
json(conn, Pleroma.Stats.get_peers()) json(conn, Pleroma.Stats.get_peers())

View File

@ -13,12 +13,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
def render("show.json", _) do def render("show.json", _) do
instance = Config.get(:instance) instance = Config.get(:instance)
%{ common_information(instance)
uri: Pleroma.Web.Endpoint.url(), |> Map.merge(%{
title: Keyword.get(instance, :name), uri: Pleroma.Web.WebFinger.host(),
description: Keyword.get(instance, :description), description: Keyword.get(instance, :description),
short_description: Keyword.get(instance, :short_description), short_description: Keyword.get(instance, :short_description),
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
email: Keyword.get(instance, :email), email: Keyword.get(instance, :email),
urls: %{ urls: %{
streaming_api: Pleroma.Web.Endpoint.websocket_url() streaming_api: Pleroma.Web.Endpoint.websocket_url()
@ -27,9 +26,9 @@ def render("show.json", _) do
thumbnail: thumbnail:
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail)) URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|> to_string, |> to_string,
languages: Keyword.get(instance, :languages, ["en"]),
registrations: Keyword.get(instance, :registrations_open), registrations: Keyword.get(instance, :registrations_open),
approval_required: Keyword.get(instance, :account_approval_required), approval_required: Keyword.get(instance, :account_approval_required),
configuration: configuration(),
# Extra (not present in Mastodon): # Extra (not present in Mastodon):
max_toot_chars: Keyword.get(instance, :limit), max_toot_chars: Keyword.get(instance, :limit),
max_media_attachments: Keyword.get(instance, :max_media_attachments), max_media_attachments: Keyword.get(instance, :max_media_attachments),
@ -41,19 +40,44 @@ def render("show.json", _) do
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image), background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
shout_limit: Config.get([:shout, :limit]), shout_limit: Config.get([:shout, :limit]),
description_limit: Keyword.get(instance, :description_limit), description_limit: Keyword.get(instance, :description_limit),
pleroma: %{ pleroma: pleroma_configuration(instance)
metadata: %{ })
account_activation_required: Keyword.get(instance, :account_activation_required), end
features: features(),
federation: federation(), def render("show2.json", _) do
fields_limits: fields_limits(), instance = Config.get(:instance)
post_formats: Config.get([:instance, :allowed_post_formats]),
birthday_required: Config.get([:instance, :birthday_required]), common_information(instance)
birthday_min_age: Config.get([:instance, :birthday_min_age]) |> Map.merge(%{
domain: Pleroma.Web.WebFinger.host(),
source_url: Pleroma.Application.repository(),
description: Keyword.get(instance, :short_description),
usage: %{users: %{active_month: Pleroma.User.active_user_count()}},
thumbnail: %{
url:
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|> to_string
}, },
stats: %{mau: Pleroma.User.active_user_count()}, configuration: configuration2(),
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) registrations: %{
} enabled: Keyword.get(instance, :registrations_open),
approval_required: Keyword.get(instance, :account_approval_required),
message: nil
},
contact: %{
email: Keyword.get(instance, :email),
account: nil
},
# Extra (not present in Mastodon):
pleroma: pleroma_configuration2(instance)
})
end
defp common_information(instance) do
%{
title: Keyword.get(instance, :name),
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
languages: Keyword.get(instance, :languages, ["en"])
} }
end end
@ -133,7 +157,7 @@ def federation do
|> Map.put(:enabled, Config.get([:instance, :federating])) |> Map.put(:enabled, Config.get([:instance, :federating]))
end end
def fields_limits do defp fields_limits do
%{ %{
max_fields: Config.get([:instance, :max_account_fields]), max_fields: Config.get([:instance, :max_account_fields]),
max_remote_fields: Config.get([:instance, :max_remote_account_fields]), max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
@ -141,4 +165,65 @@ def fields_limits do
value_length: Config.get([:instance, :account_field_value_length]) value_length: Config.get([:instance, :account_field_value_length])
} }
end end
defp configuration do
%{
statuses: %{
max_characters: Config.get([:instance, :limit]),
max_media_attachments: Config.get([:instance, :max_media_attachments])
},
media_attachments: %{
image_size_limit: Config.get([:instance, :upload_limit]),
video_size_limit: Config.get([:instance, :upload_limit])
},
polls: %{
max_options: Config.get([:instance, :poll_limits, :max_options]),
max_characters_per_option: Config.get([:instance, :poll_limits, :max_option_chars]),
min_expiration: Config.get([:instance, :poll_limits, :min_expiration]),
max_expiration: Config.get([:instance, :poll_limits, :max_expiration])
}
}
end
defp configuration2 do
configuration()
|> Map.merge(%{
urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()}
})
end
defp pleroma_configuration(instance) do
%{
metadata: %{
account_activation_required: Keyword.get(instance, :account_activation_required),
features: features(),
federation: federation(),
fields_limits: fields_limits(),
post_formats: Config.get([:instance, :allowed_post_formats]),
birthday_required: Config.get([:instance, :birthday_required]),
birthday_min_age: Config.get([:instance, :birthday_min_age])
},
stats: %{mau: Pleroma.User.active_user_count()},
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
}
end
defp pleroma_configuration2(instance) do
configuration = pleroma_configuration(instance)
configuration
|> Map.merge(%{
metadata:
configuration.metadata
|> Map.merge(%{
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
background_upload_limit: Keyword.get(instance, :background_upload_limit),
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
background_image:
Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
description_limit: Keyword.get(instance, :description_limit),
shout_limit: Config.get([:shout, :limit])
})
})
end
end end

View File

@ -783,11 +783,14 @@ defmodule Pleroma.Web.Router do
scope "/api/v2", Pleroma.Web.MastodonAPI do scope "/api/v2", Pleroma.Web.MastodonAPI do
pipe_through(:api) pipe_through(:api)
get("/search", SearchController, :search2) get("/search", SearchController, :search2)
post("/media", MediaController, :create2) post("/media", MediaController, :create2)
get("/suggestions", SuggestionController, :index2) get("/suggestions", SuggestionController, :index2)
get("/instance", InstanceController, :show2)
end end
scope "/api", Pleroma.Web do scope "/api", Pleroma.Web do

View File

@ -70,7 +70,7 @@ defp gather_aliases(%User{} = user) do
def represent_user(user, "JSON") do def represent_user(user, "JSON") do
%{ %{
"subject" => "acct:#{user.nickname}@#{domain()}", "subject" => "acct:#{user.nickname}@#{host()}",
"aliases" => gather_aliases(user), "aliases" => gather_aliases(user),
"links" => gather_links(user) "links" => gather_links(user)
} }
@ -90,13 +90,13 @@ def represent_user(user, "XML") do
:XRD, :XRD,
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
[ [
{:Subject, "acct:#{user.nickname}@#{domain()}"} {:Subject, "acct:#{user.nickname}@#{host()}"}
] ++ aliases ++ links ] ++ aliases ++ links
} }
|> XmlBuilder.to_doc() |> XmlBuilder.to_doc()
end end
defp domain do def host do
Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host() Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host()
end end

View File

@ -106,4 +106,11 @@ test "instance languages", %{conn: conn} do
|> get("/api/v1/instance") |> get("/api/v1/instance")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)
end end
test "get instance information v2", %{conn: conn} do
clear_config([:auth, :oauth_consumer_strategies], [])
assert get(conn, "/api/v2/instance")
|> json_response_and_validate_schema(200)
end
end end