Merge remote-tracking branch 'origin/develop' into paradedb-search-backend
This commit is contained in:
commit
2cc7b8356c
118 changed files with 1639 additions and 851 deletions
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Bookmark do
|
|||
schema "bookmarks" do
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.Type)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Bookmark do
|
|||
|> validate_required([:user_id, :activity_id])
|
||||
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|
||||
|> Repo.insert(
|
||||
on_conflict: [set: [folder_id: folder_id]],
|
||||
on_conflict: [set: [folder_id: folder_id, updated_at: NaiveDateTime.utc_now()]],
|
||||
conflict_target: [:user_id, :activity_id]
|
||||
)
|
||||
end
|
||||
|
|
@ -76,11 +76,4 @@ defmodule Pleroma.Bookmark do
|
|||
|> Repo.one()
|
||||
|> Repo.delete()
|
||||
end
|
||||
|
||||
def set_folder(bookmark, folder_id) do
|
||||
bookmark
|
||||
|> cast(%{folder_id: folder_id}, [:folder_id])
|
||||
|> validate_required([:folder_id])
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.BookmarkFolder do
|
|||
alias Pleroma.User
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
|
||||
@primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
|
||||
|
||||
schema "bookmark_folders" do
|
||||
field(:name, :string)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
|
|||
import Pleroma.Web.Gettext
|
||||
|
||||
alias __MODULE__
|
||||
alias Pleroma.EctoType.Config.RateLimit
|
||||
alias Pleroma.Repo
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
|
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|
|||
|> cast(params, [:key, :group, :value])
|
||||
|> validate_required([:key, :group, :value])
|
||||
|> unique_constraint(:key, name: :config_group_key_index)
|
||||
|> validate_rate_limit()
|
||||
end
|
||||
|
||||
defp validate_rate_limit(changeset) do
|
||||
group = get_field(changeset, :group)
|
||||
key = get_field(changeset, :key)
|
||||
|
||||
if group == :pleroma and key == :rate_limit do
|
||||
value = get_field(changeset, :value)
|
||||
|
||||
case normalize_rate_limit(value) do
|
||||
{:ok, normalized_value} ->
|
||||
put_change(changeset, :value, normalized_value)
|
||||
|
||||
{:error, {limiter_name, reason}} ->
|
||||
add_error(
|
||||
changeset,
|
||||
:value,
|
||||
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
|
||||
)
|
||||
end
|
||||
else
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_rate_limit(nil), do: {:ok, nil}
|
||||
|
||||
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
|
||||
|
||||
defp normalize_rate_limit(value) when is_list(value) do
|
||||
if Keyword.keyword?(value) do
|
||||
value
|
||||
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
|
||||
case RateLimit.cast_with_error(limiter_value) do
|
||||
{:ok, normalized_limiter_value} ->
|
||||
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
|
||||
|
||||
{:error, reason} ->
|
||||
{:halt, {:error, {limiter_name, reason}}}
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
{:ok, acc} -> {:ok, Enum.reverse(acc)}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
else
|
||||
{:error, {:rate_limit, "must be a keyword list"}}
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
|
||||
|
||||
defp create(params) do
|
||||
%ConfigDB{}
|
||||
|> changeset(params)
|
||||
|
|
|
|||
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.EctoType.Config.RateLimit do
|
||||
@moduledoc false
|
||||
|
||||
use Ecto.Type
|
||||
|
||||
@type t ::
|
||||
nil
|
||||
| {non_neg_integer(), non_neg_integer()}
|
||||
| [{non_neg_integer(), non_neg_integer()}]
|
||||
|
||||
@impl true
|
||||
def type, do: :term
|
||||
|
||||
@impl true
|
||||
def cast(value) do
|
||||
case cast_with_error(value) do
|
||||
{:ok, normalized} -> {:ok, normalized}
|
||||
{:error, _reason} -> :error
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def load(value), do: cast(value)
|
||||
|
||||
@impl true
|
||||
def dump(value), do: cast(value)
|
||||
|
||||
@spec cast_with_error(term()) :: {:ok, t()} | {:error, String.t()}
|
||||
def cast_with_error(nil), do: {:ok, nil}
|
||||
|
||||
def cast_with_error({scale, limit}) do
|
||||
with {:ok, scale} <- parse_integer(scale, "scale"),
|
||||
{:ok, limit} <- parse_integer(limit, "limit"),
|
||||
true <- scale >= 1 and limit >= 1 do
|
||||
{:ok, {scale, limit}}
|
||||
else
|
||||
false -> {:error, "scale and limit must be >= 1"}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def cast_with_error([{_, _} = unauth, {_, _} = auth]) do
|
||||
with {:ok, unauth} <- cast_with_error(unauth),
|
||||
{:ok, auth} <- cast_with_error(auth) do
|
||||
{:ok, [unauth, auth]}
|
||||
else
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def cast_with_error(_),
|
||||
do:
|
||||
{:error, "must be a {scale, limit} tuple, a [{scale, limit}, {scale, limit}] list, or nil"}
|
||||
|
||||
defp parse_integer(value, _label) when is_integer(value), do: {:ok, value}
|
||||
|
||||
defp parse_integer(value, label) when is_binary(value) do
|
||||
value = String.trim(value)
|
||||
|
||||
case Integer.parse(value) do
|
||||
{number, ""} -> {:ok, number}
|
||||
_ -> {:error, "#{label} must be an integer"}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_integer(_value, label), do: {:error, "#{label} must be an integer"}
|
||||
end
|
||||
|
|
@ -21,10 +21,13 @@ defmodule Pleroma.Gopher.Server do
|
|||
|
||||
def init([ip, port]) do
|
||||
Logger.info("Starting gopher server on #{port}")
|
||||
Process.flag(:trap_exit, true)
|
||||
|
||||
listener = :gopher
|
||||
|
||||
{:ok, _pid} =
|
||||
:ranch.start_listener(
|
||||
:gopher,
|
||||
listener,
|
||||
:ranch_tcp,
|
||||
%{
|
||||
num_acceptors: 100,
|
||||
|
|
@ -35,7 +38,11 @@ defmodule Pleroma.Gopher.Server do
|
|||
[]
|
||||
)
|
||||
|
||||
{:ok, %{ip: ip, port: port}}
|
||||
{:ok, %{ip: ip, port: port, listener: listener}}
|
||||
end
|
||||
|
||||
def terminate(_reason, state) do
|
||||
:ranch.stop_listener(state.listener)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ defmodule Pleroma.List do
|
|||
field(:title, :string)
|
||||
field(:following, {:array, :string}, default: [])
|
||||
field(:ap_id, :string)
|
||||
field(:exclusive, :boolean, default: false)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def title_changeset(list, attrs \\ %{}) do
|
||||
def update_changeset(list, attrs \\ %{}) do
|
||||
list
|
||||
|> cast(attrs, [:title])
|
||||
|> cast(attrs, [:title, :exclusive])
|
||||
|> validate_required([:title])
|
||||
end
|
||||
|
||||
|
|
@ -91,14 +92,14 @@ defmodule Pleroma.List do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
def rename(%Pleroma.List{} = list, title) do
|
||||
def update(%Pleroma.List{} = list, params) do
|
||||
list
|
||||
|> title_changeset(%{title: title})
|
||||
|> update_changeset(params)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def create(title, %User{} = creator) do
|
||||
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title})
|
||||
def create(params, %User{} = creator) do
|
||||
changeset = update_changeset(%Pleroma.List{user_id: creator.id}, params)
|
||||
|
||||
if changeset.valid? do
|
||||
Repo.transaction(fn ->
|
||||
|
|
@ -149,4 +150,14 @@ defmodule Pleroma.List do
|
|||
end
|
||||
|
||||
def member?(_, _), do: false
|
||||
|
||||
def get_exclusive_list_members(%User{id: user_id}) do
|
||||
Pleroma.List
|
||||
|> where([l], l.user_id == ^user_id)
|
||||
|> where([l], l.exclusive == true)
|
||||
|> select([l], l.following)
|
||||
|> Repo.all()
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ defmodule Pleroma.Marker do
|
|||
|
||||
defp get_marker(user, timeline) do
|
||||
case Repo.find_resource(get_query(user, timeline)) do
|
||||
{:ok, marker} -> %__MODULE__{marker | user: user}
|
||||
{:ok, %__MODULE__{} = marker} -> %{marker | user: user}
|
||||
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do
|
|||
alias Pleroma.User
|
||||
|
||||
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
|
||||
settings =
|
||||
%Settings{} =
|
||||
settings =
|
||||
changeset
|
||||
|> Ecto.Changeset.apply_changes()
|
||||
|> MFA.fetch_settings()
|
||||
|
|
@ -20,20 +21,20 @@ defmodule Pleroma.MFA.Changeset do
|
|||
end
|
||||
end
|
||||
|
||||
def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
||||
def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||
user
|
||||
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
|
||||
end
|
||||
|
||||
def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
||||
totp_settings = %Settings.TOTP{settings.totp | confirmed: true}
|
||||
def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||
totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true}
|
||||
|
||||
user
|
||||
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
|
||||
end
|
||||
|
||||
def setup_totp(%User{} = user, attrs) do
|
||||
mfa_settings = MFA.fetch_settings(user)
|
||||
%Settings{} = mfa_settings = MFA.fetch_settings(user)
|
||||
|
||||
totp_settings =
|
||||
%Settings.TOTP{}
|
||||
|
|
@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do
|
|||
def cast_backup_codes(%User{} = user, codes) do
|
||||
user
|
||||
|> put_change(%Settings{
|
||||
user.multi_factor_authentication_settings
|
||||
(%Settings{} = user.multi_factor_authentication_settings)
|
||||
| backup_codes: codes
|
||||
})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||
@behaviour Pleroma.ReverseProxy.Client
|
||||
@redirect_limit 5
|
||||
|
||||
require Logger
|
||||
|
||||
# In-app redirect handler to avoid Hackney redirect bugs:
|
||||
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
|
||||
|
|
@ -31,26 +34,15 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
|||
|
||||
if opts[:follow_redirect] != false do
|
||||
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
||||
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
|
||||
res = :hackney.request(method, url, headers, body, req_opts)
|
||||
|
||||
case res do
|
||||
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
||||
:hackney.request(
|
||||
method,
|
||||
absolute_redirect_url(url, resp_headers),
|
||||
headers,
|
||||
body,
|
||||
req_opts
|
||||
)
|
||||
redirect(url, resp_headers, env, @redirect_limit)
|
||||
|
||||
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
||||
:hackney.request(
|
||||
method,
|
||||
absolute_redirect_url(url, resp_headers),
|
||||
headers,
|
||||
body,
|
||||
req_opts
|
||||
)
|
||||
redirect(url, resp_headers, env, @redirect_limit)
|
||||
|
||||
_ ->
|
||||
res
|
||||
|
|
@ -71,4 +63,32 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
|||
|
||||
@impl true
|
||||
def close(ref), do: :hackney.close(ref)
|
||||
|
||||
defp redirect(url, resp_headers, env, limit) when limit == 0 do
|
||||
new_url = absolute_redirect_url(url, resp_headers)
|
||||
|
||||
Logger.debug(
|
||||
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
|
||||
)
|
||||
|
||||
:hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||
end
|
||||
|
||||
defp redirect(url, resp_headers, env, limit) do
|
||||
new_url = absolute_redirect_url(url, resp_headers)
|
||||
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
|
||||
|
||||
res = :hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||
|
||||
case res do
|
||||
{:ok, code, new_resp_headers, _client} when code in @redirect_statuses ->
|
||||
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||
|
||||
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
|
||||
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||
|
||||
_ ->
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,28 @@
|
|||
defmodule Pleroma.Search do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Workers.SearchIndexingWorker
|
||||
|
||||
def add_to_index(%Pleroma.Activity{id: activity_id}) do
|
||||
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
||||
|> Oban.insert()
|
||||
@spec add_to_index(Activity.t()) :: {:ok, Oban.Job.t() | :noop} | {:error, Oban.Job.changeset()}
|
||||
def add_to_index(%Activity{id: activity_id, object: %Object{} = object} = activity) do
|
||||
with {_, true} <- {:indexable, indexable?(activity)},
|
||||
{_, "public"} <- {:visibility, Visibility.get_visibility(object)} do
|
||||
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
||||
|> Oban.insert()
|
||||
else
|
||||
_ -> {:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
||||
def add_to_index(%Activity{id: activity_id}) do
|
||||
case Activity.get_by_id_with_object(activity_id) do
|
||||
%Activity{} = preloaded -> add_to_index(preloaded)
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
@spec remove_from_index(Object.t()) :: {:ok, Oban.Job.t()} | {:error, Oban.Job.changeset()}
|
||||
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
||||
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|
||||
|> Oban.insert()
|
||||
|
|
@ -20,4 +37,7 @@ defmodule Pleroma.Search do
|
|||
search_module = Pleroma.Config.get([Pleroma.Search, :module])
|
||||
search_module.healthcheck_endpoints()
|
||||
end
|
||||
|
||||
defp indexable?(%Activity{data: %{"type" => "Create"}}), do: true
|
||||
defp indexable?(_), do: false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Search.Meilisearch do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config.Getting, as: Config
|
||||
alias Pleroma.Object
|
||||
|
||||
import Pleroma.Search.DatabaseSearch
|
||||
import Ecto.Query
|
||||
|
|
@ -156,28 +157,23 @@ defmodule Pleroma.Search.Meilisearch do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def add_to_index(activity) do
|
||||
maybe_search_data = object_to_search_data(activity.object)
|
||||
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||
search_data = object_to_search_data(object)
|
||||
|
||||
if activity.data["type"] == "Create" and maybe_search_data do
|
||||
result =
|
||||
meili_put(
|
||||
"/indexes/objects/documents",
|
||||
[maybe_search_data]
|
||||
)
|
||||
result =
|
||||
meili_put(
|
||||
"/indexes/objects/documents",
|
||||
[search_data]
|
||||
)
|
||||
|
||||
with {:ok, %{"status" => "enqueued"}} <- result do
|
||||
# Added successfully
|
||||
:ok
|
||||
else
|
||||
_ ->
|
||||
# There was an error, report it
|
||||
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
||||
{:error, result}
|
||||
end
|
||||
else
|
||||
# The post isn't something we can search, that's ok
|
||||
with {:ok, %{"status" => "enqueued"}} <- result do
|
||||
# Added successfully
|
||||
:ok
|
||||
else
|
||||
_ ->
|
||||
# There was an error, report it
|
||||
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
||||
{:error, result}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Search.QdrantSearch do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config.Getting, as: Config
|
||||
alias Pleroma.Object
|
||||
|
||||
alias __MODULE__.OpenAIClient
|
||||
alias __MODULE__.QdrantClient
|
||||
|
|
@ -82,23 +83,18 @@ defmodule Pleroma.Search.QdrantSearch do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def add_to_index(activity) do
|
||||
# This will only index public or unlisted notes
|
||||
maybe_search_data = object_to_search_data(activity.object)
|
||||
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||
search_data = object_to_search_data(object)
|
||||
|
||||
if activity.data["type"] == "Create" and maybe_search_data do
|
||||
with {:ok, embedding} <- get_embedding(maybe_search_data.content),
|
||||
{:ok, %{status: 200}} <-
|
||||
QdrantClient.put(
|
||||
"/collections/posts/points",
|
||||
build_index_payload(activity, embedding)
|
||||
) do
|
||||
:ok
|
||||
else
|
||||
e -> {:error, e}
|
||||
end
|
||||
else
|
||||
with {:ok, embedding} <- get_embedding(search_data.content),
|
||||
{:ok, %{status: 200}} <-
|
||||
QdrantClient.put(
|
||||
"/collections/posts/points",
|
||||
build_index_payload(activity, embedding)
|
||||
) do
|
||||
:ok
|
||||
else
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
|
|||
def store(upload, opts \\ []) do
|
||||
opts = get_opts(opts)
|
||||
|
||||
with {:ok, upload} <- prepare_upload(upload, opts),
|
||||
with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts),
|
||||
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
||||
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
||||
description = get_description(upload),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Utils do
|
|||
dir
|
||||
|> File.ls!()
|
||||
|> Enum.map(&Path.join(dir, &1))
|
||||
|> Kernel.ParallelCompiler.compile()
|
||||
|> Kernel.ParallelCompiler.compile(return_diagnostics: true)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -1618,6 +1618,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
||||
defp normalize_image(_), do: nil
|
||||
|
||||
defp normalize_also_known_as(urls) when is_list(urls), do: urls
|
||||
defp normalize_also_known_as(url) when is_binary(url), do: [url]
|
||||
defp normalize_also_known_as(nil), do: []
|
||||
|
||||
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
||||
Map.put(map, "name", description)
|
||||
end
|
||||
|
|
@ -1693,7 +1697,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
featured_address: featured_address,
|
||||
bio: data["summary"] || "",
|
||||
actor_type: actor_type,
|
||||
also_known_as: Map.get(data, "alsoKnownAs", []),
|
||||
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
|
||||
public_key: public_key,
|
||||
inbox: data["inbox"],
|
||||
shared_inbox: shared_inbox,
|
||||
|
|
|
|||
|
|
@ -332,21 +332,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
|
||||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||
def announce(actor, object, options \\ []) do
|
||||
public? = Keyword.get(options, :public, false)
|
||||
visibility = Keyword.get(options, :visibility, "public")
|
||||
|
||||
to =
|
||||
cond do
|
||||
actor.ap_id == Relay.ap_id() ->
|
||||
[actor.follower_address]
|
||||
|
||||
public? and Visibility.local_public?(object) ->
|
||||
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
||||
|
||||
public? ->
|
||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
||||
|
||||
true ->
|
||||
[actor.follower_address, object.data["actor"]]
|
||||
{to, cc} =
|
||||
if actor.ap_id == Relay.ap_id() do
|
||||
{[actor.follower_address], []}
|
||||
else
|
||||
Pleroma.Web.CommonAPI.Utils.get_to_and_cc_for_visibility(
|
||||
visibility,
|
||||
actor.follower_address,
|
||||
nil,
|
||||
[object.data["actor"]]
|
||||
)
|
||||
end
|
||||
|
||||
{:ok,
|
||||
|
|
@ -355,6 +352,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
"actor" => actor.ap_id,
|
||||
"object" => object.data["id"],
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"context" => object.data["context"],
|
||||
"type" => "Announce",
|
||||
"published" => Utils.make_date()
|
||||
|
|
|
|||
|
|
@ -35,32 +35,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
def render("endpoints.json", _), do: %{}
|
||||
|
||||
def render("service.json", %{user: user}) do
|
||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
endpoints = render("endpoints.json", %{user: user})
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
Map.merge(common_actor_fields(user), %{
|
||||
"type" => "Application",
|
||||
"following" => "#{user.ap_id}/following",
|
||||
"followers" => "#{user.ap_id}/followers",
|
||||
"inbox" => "#{user.ap_id}/inbox",
|
||||
"outbox" => "#{user.ap_id}/outbox",
|
||||
"name" => "Pleroma",
|
||||
"summary" =>
|
||||
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
||||
"url" => user.ap_id,
|
||||
"manuallyApprovesFollowers" => false,
|
||||
"publicKey" => %{
|
||||
"id" => "#{user.ap_id}#main-key",
|
||||
"owner" => user.ap_id,
|
||||
"publicKeyPem" => public_key
|
||||
},
|
||||
"endpoints" => endpoints,
|
||||
"invisible" => User.invisible?(user)
|
||||
}
|
||||
})
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
|
@ -77,13 +59,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
end
|
||||
|
||||
def render("user.json", %{user: user}) do
|
||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
user = User.sanitize_html(user)
|
||||
|
||||
endpoints = render("endpoints.json", %{user: user})
|
||||
|
||||
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
||||
|
||||
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||
|
|
@ -102,25 +79,9 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
do: Date.to_iso8601(user.birthday),
|
||||
else: nil
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
"type" => user.actor_type,
|
||||
"following" => "#{user.ap_id}/following",
|
||||
"followers" => "#{user.ap_id}/followers",
|
||||
"inbox" => "#{user.ap_id}/inbox",
|
||||
"outbox" => "#{user.ap_id}/outbox",
|
||||
Map.merge(common_actor_fields(user), %{
|
||||
"featured" => "#{user.ap_id}/collections/featured",
|
||||
"preferredUsername" => user.nickname,
|
||||
"name" => user.name,
|
||||
"summary" => user.bio,
|
||||
"url" => user.ap_id,
|
||||
"manuallyApprovesFollowers" => user.is_locked,
|
||||
"publicKey" => %{
|
||||
"id" => "#{user.ap_id}#main-key",
|
||||
"owner" => user.ap_id,
|
||||
"publicKeyPem" => public_key
|
||||
},
|
||||
"endpoints" => endpoints,
|
||||
"attachment" => fields,
|
||||
"tag" => emoji_tags,
|
||||
# Note: key name is indeed "discoverable" (not an error)
|
||||
|
|
@ -130,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"vcard:bday" => birthday,
|
||||
"webfinger" => "acct:#{User.full_nickname(user)}",
|
||||
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
|
||||
}
|
||||
})
|
||||
|> Map.merge(
|
||||
maybe_make_image(
|
||||
&User.avatar_url/2,
|
||||
|
|
@ -309,6 +270,33 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
defp common_actor_fields(%User{} = user) do
|
||||
endpoints = render("endpoints.json", %{user: user})
|
||||
|
||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
"type" => user.actor_type,
|
||||
"following" => "#{user.ap_id}/following",
|
||||
"followers" => "#{user.ap_id}/followers",
|
||||
"inbox" => "#{user.ap_id}/inbox",
|
||||
"outbox" => "#{user.ap_id}/outbox",
|
||||
"name" => user.name,
|
||||
"summary" => user.bio,
|
||||
"url" => user.ap_id,
|
||||
"manuallyApprovesFollowers" => user.is_locked,
|
||||
"endpoints" => endpoints,
|
||||
"publicKey" => %{
|
||||
"id" => "#{user.ap_id}#main-key",
|
||||
"owner" => user.ap_id,
|
||||
"publicKeyPem" => public_key
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp maybe_put_total_items(map, false, _total), do: map
|
||||
|
||||
defp maybe_put_total_items(map, true, total) do
|
||||
|
|
|
|||
|
|
@ -106,7 +106,14 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
|
|||
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
|
||||
end
|
||||
|
||||
defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
|
||||
defp cast_and_validate(
|
||||
spec,
|
||||
operation,
|
||||
%Conn{} = conn,
|
||||
content_type,
|
||||
false = _strict,
|
||||
cast_opts
|
||||
) do
|
||||
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
|
||||
{:ok, conn} ->
|
||||
{:ok, conn}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
summary: "Create a list",
|
||||
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
|
||||
operationId: "ListController.create",
|
||||
requestBody: create_update_request(),
|
||||
requestBody: create_request(),
|
||||
security: [%{"oAuth" => ["write:lists"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("List", "application/json", List),
|
||||
|
|
@ -68,7 +68,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
description: "Change the title of a list",
|
||||
operationId: "ListController.update",
|
||||
parameters: [id_param()],
|
||||
requestBody: create_update_request(),
|
||||
requestBody: update_request(),
|
||||
security: [%{"oAuth" => ["write:lists"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("List", "application/json", List),
|
||||
|
|
@ -164,14 +164,15 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp create_update_request do
|
||||
defp create_request do
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
description: "POST body for creating or updating a List",
|
||||
description: "POST body for creating a List",
|
||||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string, description: "List title"}
|
||||
title: %Schema{type: :string, description: "List title"},
|
||||
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||
},
|
||||
required: [:title]
|
||||
},
|
||||
|
|
@ -179,6 +180,21 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp update_request do
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
description: "PUT body for updating a List",
|
||||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string, description: "List title"},
|
||||
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||
}
|
||||
},
|
||||
required: true
|
||||
)
|
||||
end
|
||||
|
||||
defp add_remove_accounts_request(required) when is_boolean(required) do
|
||||
request_body(
|
||||
"Parameters",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaUtilOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
%Operation{
|
||||
tags: ["Custom emojis"],
|
||||
summary: "List all custom emojis",
|
||||
operationId: "UtilController.emoji",
|
||||
operationId: "PleromaAPI.UtilController.emoji",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -48,7 +48,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
%Operation{
|
||||
tags: ["Others"],
|
||||
summary: "Dump frontend configurations",
|
||||
operationId: "UtilController.frontend_configurations",
|
||||
operationId: "PleromaAPI.UtilController.frontend_configurations",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Change account password",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.change_password",
|
||||
operationId: "PleromaAPI.UtilController.change_password",
|
||||
requestBody: request_body("Parameters", change_password_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Change account email",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.change_email",
|
||||
operationId: "PleromaAPI.UtilController.change_email",
|
||||
requestBody: request_body("Parameters", change_email_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -141,7 +141,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Settings"],
|
||||
summary: "Update Notification Settings",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.update_notification_settings",
|
||||
operationId: "PleromaAPI.UtilController.update_notification_settings",
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:block_from_strangers,
|
||||
|
|
@ -173,7 +173,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Disable Account",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.disable_account",
|
||||
operationId: "PleromaAPI.UtilController.disable_account",
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Password")
|
||||
],
|
||||
|
|
@ -193,7 +193,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Delete Account",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.delete_account",
|
||||
operationId: "PleromaAPI.UtilController.delete_account",
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Password")
|
||||
],
|
||||
|
|
@ -212,7 +212,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
def captcha_operation do
|
||||
%Operation{
|
||||
summary: "Get a captcha",
|
||||
operationId: "UtilController.captcha",
|
||||
operationId: "PleromaAPI.UtilController.captcha",
|
||||
tags: ["Others"],
|
||||
parameters: [],
|
||||
responses: %{
|
||||
|
|
@ -226,7 +226,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Move account",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.move_account",
|
||||
operationId: "PleromaAPI.UtilController.move_account",
|
||||
requestBody: request_body("Parameters", move_account_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -262,7 +262,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "List account aliases",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
operationId: "UtilController.list_aliases",
|
||||
operationId: "PleromaAPI.UtilController.list_aliases",
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
|
|
@ -286,7 +286,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Add an alias to this account",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.add_alias",
|
||||
operationId: "PleromaAPI.UtilController.add_alias",
|
||||
requestBody: request_body("Parameters", add_alias_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -326,7 +326,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Account credentials"],
|
||||
summary: "Delete an alias from this account",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.delete_alias",
|
||||
operationId: "PleromaAPI.UtilController.delete_alias",
|
||||
requestBody: request_body("Parameters", delete_alias_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
@ -366,7 +366,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
tags: ["Others"],
|
||||
summary: "Quick status check on the instance",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.healthcheck",
|
||||
operationId: "PleromaAPI.UtilController.healthcheck",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
|
||||
|
|
@ -376,52 +376,6 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
}
|
||||
end
|
||||
|
||||
def remote_subscribe_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Remote Subscribe",
|
||||
operationId: "UtilController.remote_subscribe",
|
||||
parameters: [],
|
||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||
}
|
||||
end
|
||||
|
||||
def remote_interaction_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Remote interaction",
|
||||
operationId: "UtilController.remote_interaction",
|
||||
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp remote_interaction_request do
|
||||
%Schema{
|
||||
title: "RemoteInteractionRequest",
|
||||
description: "POST body for remote interaction",
|
||||
type: :object,
|
||||
required: [:ap_id, :profile],
|
||||
properties: %{
|
||||
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
||||
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def show_subscribe_form_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Show remote subscribe form",
|
||||
operationId: "UtilController.show_subscribe_form",
|
||||
parameters: [],
|
||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||
}
|
||||
end
|
||||
|
||||
defp delete_account_request do
|
||||
%Schema{
|
||||
title: "AccountDeleteRequest",
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.RemoteInteractionOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def remote_interaction_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Remote interaction",
|
||||
operationId: "RemoteInteractionController.remote_interaction",
|
||||
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp remote_interaction_request do
|
||||
%Schema{
|
||||
title: "RemoteInteractionRequest",
|
||||
description: "POST body for remote interaction",
|
||||
type: :object,
|
||||
required: [:ap_id, :profile],
|
||||
properties: %{
|
||||
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
||||
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def follow_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Display follow form",
|
||||
operationId: "RemoteInteractionController.follow",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
200 => Operation.response("Web Page", "text/html", %Schema{type: :string}),
|
||||
302 => Operation.response("Redirect to the status page", nil, nil)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def do_follow_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Perform follow activity",
|
||||
operationId: "RemoteInteractionController.do_follow",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
200 => Operation.response("Web page", "text/html", %Schema{type: :string}),
|
||||
302 => Operation.response("Redirect to the account page", nil, nil)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def authorize_interaction_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Authorize remote interaction",
|
||||
operationId: "RemoteInteractionController.authorize_interaction",
|
||||
parameters: [],
|
||||
responses: %{
|
||||
302 => Operation.response("Redirect to remote_interaction path", nil, nil)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def show_subscribe_form_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Show remote subscribe form",
|
||||
operationId: "RemoteInteractionController.show_subscribe_form",
|
||||
parameters: [],
|
||||
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
|
||||
}
|
||||
end
|
||||
|
||||
def remote_subscribe_operation do
|
||||
%Operation{
|
||||
tags: ["Remote interaction"],
|
||||
summary: "Remote Subscribe",
|
||||
operationId: "RemoteInteractionController.remote_subscribe",
|
||||
parameters: [],
|
||||
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -17,11 +17,11 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||
def call(conn, errors) do
|
||||
errors =
|
||||
Enum.map(errors, fn
|
||||
%{name: nil, reason: :invalid_enum} = err ->
|
||||
%OpenApiSpex.Cast.Error{err | name: err.value}
|
||||
%OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err ->
|
||||
%{err | name: err.value}
|
||||
|
||||
%{name: nil} = err ->
|
||||
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
||||
%OpenApiSpex.Cast.Error{name: nil} = err ->
|
||||
%{err | name: List.last(err.path)}
|
||||
|
||||
err ->
|
||||
err
|
||||
|
|
|
|||
|
|
@ -15,12 +15,18 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BookmarkFolder do
|
|||
properties: %{
|
||||
id: FlakeID,
|
||||
name: %Schema{type: :string, description: "Folder name"},
|
||||
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true}
|
||||
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true},
|
||||
emoji_url: %Schema{
|
||||
type: :string,
|
||||
description: "URL of the folder emoji if it's a custom emoji, null otherwise",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"id" => "9toJCu5YZW7O7gfvH6",
|
||||
"name" => "Read later",
|
||||
"emoji" => nil
|
||||
"emoji" => nil,
|
||||
"emoji_url" => nil
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.List do
|
|||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "The internal database ID of the list"},
|
||||
title: %Schema{type: :string, description: "The user-defined title of the list"}
|
||||
title: %Schema{type: :string, description: "The user-defined title of the list"},
|
||||
exclusive: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether members of the list should be removed from the “Home” feed"
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"id" => "12249",
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||
object = %Object{} <- Object.normalize(activity, fetch: false),
|
||||
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
||||
public = public_announce?(object, params),
|
||||
{:ok, announce, _} <- Builder.announce(user, object, public: public),
|
||||
visibility = announce_visibility(object, params),
|
||||
{:ok, announce, _} <- Builder.announce(user, object, visibility: visibility),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
|
@ -407,13 +407,11 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp public_announce?(_, %{visibility: visibility})
|
||||
when visibility in ~w{public unlisted private direct},
|
||||
do: visibility in ~w(public unlisted)
|
||||
def announce_visibility(_, %{visibility: visibility})
|
||||
when visibility in ~w{public unlisted private direct local},
|
||||
do: visibility
|
||||
|
||||
defp public_announce?(object, _) do
|
||||
Visibility.public?(object)
|
||||
end
|
||||
def announce_visibility(object, _), do: Visibility.get_visibility(object)
|
||||
|
||||
@spec get_visibility(map(), map() | nil, Participation.t() | nil) ::
|
||||
{String.t() | nil, String.t() | nil}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|> validate()
|
||||
end
|
||||
|
||||
defp listen_object(draft) do
|
||||
defp listen_object(%__MODULE__{} = draft) do
|
||||
object =
|
||||
draft.params
|
||||
|> Map.take([:album, :artist, :title, :length])
|
||||
|
|
@ -99,34 +99,34 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|> Map.put("cc", draft.cc)
|
||||
|> Map.put("actor", draft.user.ap_id)
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
%{draft | object: object}
|
||||
end
|
||||
|
||||
defp put_params(draft, params) do
|
||||
defp put_params(%__MODULE__{} = draft, params) do
|
||||
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||
%__MODULE__{draft | params: params}
|
||||
%{draft | params: params}
|
||||
end
|
||||
|
||||
defp status(%{params: %{status: status}} = draft) do
|
||||
%__MODULE__{draft | status: String.trim(status)}
|
||||
defp status(%__MODULE__{params: %{status: status}} = draft) do
|
||||
%{draft | status: String.trim(status)}
|
||||
end
|
||||
|
||||
defp summary(%{params: params} = draft) do
|
||||
%__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}
|
||||
defp summary(%__MODULE__{params: params} = draft) do
|
||||
%{draft | summary: Map.get(params, :spoiler_text, "")}
|
||||
end
|
||||
|
||||
defp full_payload(%{status: status, summary: summary} = draft) do
|
||||
defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do
|
||||
full_payload = String.trim(status <> summary)
|
||||
|
||||
case Utils.validate_character_limit(full_payload, draft.attachments) do
|
||||
:ok -> %__MODULE__{draft | full_payload: full_payload}
|
||||
:ok -> %{draft | full_payload: full_payload}
|
||||
{:error, message} -> add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp attachments(%{params: params} = draft) do
|
||||
defp attachments(%__MODULE__{params: params} = draft) do
|
||||
attachments = Utils.attachments_from_ids(params, draft.user)
|
||||
draft = %__MODULE__{draft | attachments: attachments}
|
||||
draft = %{draft | attachments: attachments}
|
||||
|
||||
case Utils.validate_attachments_count(attachments) do
|
||||
:ok -> draft
|
||||
|
|
@ -134,9 +134,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
end
|
||||
end
|
||||
|
||||
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
|
||||
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft
|
||||
|
||||
defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do
|
||||
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft)
|
||||
when is_binary(id) do
|
||||
# If a post was deleted all its activities (except the newly added Delete) are purged too,
|
||||
# thus lookup by Create db ID will yield nil just as if it never existed in the first place.
|
||||
#
|
||||
|
|
@ -148,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
true <- Visibility.visible_for_user?(activity, draft.user),
|
||||
{_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do
|
||||
%__MODULE__{draft | in_reply_to: activity}
|
||||
%{draft | in_reply_to: activity}
|
||||
else
|
||||
nil ->
|
||||
add_error(draft, dgettext("errors", "Cannot reply to a deleted status"))
|
||||
|
|
@ -166,40 +167,43 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
end
|
||||
end
|
||||
|
||||
defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do
|
||||
%__MODULE__{draft | in_reply_to: in_reply_to}
|
||||
defp in_reply_to(
|
||||
%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft
|
||||
) do
|
||||
%{draft | in_reply_to: in_reply_to}
|
||||
end
|
||||
|
||||
defp in_reply_to(draft), do: draft
|
||||
|
||||
defp quote_post(%{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do
|
||||
defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft)
|
||||
when not_empty_string(id) do
|
||||
case Activity.get_by_id_with_object(id) do
|
||||
%Activity{} = activity ->
|
||||
%__MODULE__{draft | quote_post: activity}
|
||||
%{draft | quote_post: activity}
|
||||
|
||||
_ ->
|
||||
draft
|
||||
end
|
||||
end
|
||||
|
||||
defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do
|
||||
defp quote_post(%__MODULE__{params: %{quote_id: id}} = draft) when not_empty_string(id) do
|
||||
quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)})
|
||||
end
|
||||
|
||||
defp quote_post(draft), do: draft
|
||||
|
||||
defp in_reply_to_conversation(draft) do
|
||||
defp in_reply_to_conversation(%__MODULE__{} = draft) do
|
||||
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
|
||||
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
|
||||
%{draft | in_reply_to_conversation: in_reply_to_conversation}
|
||||
end
|
||||
|
||||
defp visibility(%{params: params} = draft) do
|
||||
defp visibility(%__MODULE__{params: params} = draft) do
|
||||
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
|
||||
{visibility, "direct"} when visibility != "direct" ->
|
||||
add_error(draft, dgettext("errors", "The message visibility must be direct"))
|
||||
|
||||
{visibility, _} ->
|
||||
%__MODULE__{draft | visibility: visibility}
|
||||
%{draft | visibility: visibility}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -215,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
false
|
||||
end
|
||||
|
||||
defp quoting_visibility(%{quote_post: %Activity{}} = draft) do
|
||||
defp quoting_visibility(%__MODULE__{quote_post: %Activity{}} = draft) do
|
||||
with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false),
|
||||
true <- can_quote?(draft, object, Visibility.get_visibility(object)) do
|
||||
draft
|
||||
|
|
@ -226,24 +230,24 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|
||||
defp quoting_visibility(draft), do: draft
|
||||
|
||||
defp expires_at(draft) do
|
||||
defp expires_at(%__MODULE__{} = draft) do
|
||||
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
|
||||
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
|
||||
{:ok, expires_at} -> %{draft | expires_at: expires_at}
|
||||
{:error, message} -> add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp poll(draft) do
|
||||
defp poll(%__MODULE__{} = draft) do
|
||||
case Utils.make_poll_data(draft.params) do
|
||||
{:ok, {poll, poll_emoji}} ->
|
||||
%__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
|
||||
%{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
|
||||
|
||||
{:error, message} ->
|
||||
add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp content(%{mentions: mentions} = draft) do
|
||||
defp content(%__MODULE__{mentions: mentions} = draft) do
|
||||
{content_html, mentioned_users, tags} = Utils.make_content_html(draft)
|
||||
|
||||
mentioned_ap_ids =
|
||||
|
|
@ -254,25 +258,25 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|> Kernel.++(mentioned_ap_ids)
|
||||
|> Utils.get_addressed_users(draft.params[:to])
|
||||
|
||||
%__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags}
|
||||
%{draft | content_html: content_html, mentions: mentions, tags: tags}
|
||||
end
|
||||
|
||||
defp to_and_cc(draft) do
|
||||
defp to_and_cc(%__MODULE__{} = draft) do
|
||||
{to, cc} = Utils.get_to_and_cc(draft)
|
||||
%__MODULE__{draft | to: to, cc: cc}
|
||||
%{draft | to: to, cc: cc}
|
||||
end
|
||||
|
||||
defp context(draft) do
|
||||
defp context(%__MODULE__{} = draft) do
|
||||
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
|
||||
%__MODULE__{draft | context: context}
|
||||
%{draft | context: context}
|
||||
end
|
||||
|
||||
defp sensitive(draft) do
|
||||
defp sensitive(%__MODULE__{} = draft) do
|
||||
sensitive = draft.params[:sensitive]
|
||||
%__MODULE__{draft | sensitive: sensitive}
|
||||
%{draft | sensitive: sensitive}
|
||||
end
|
||||
|
||||
defp language(draft) do
|
||||
defp language(%__MODULE__{} = draft) do
|
||||
language =
|
||||
with language <- draft.params[:language],
|
||||
true <- good_locale_code?(language) do
|
||||
|
|
@ -281,10 +285,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
_ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
|
||||
end
|
||||
|
||||
%__MODULE__{draft | language: language}
|
||||
%{draft | language: language}
|
||||
end
|
||||
|
||||
defp object(draft) do
|
||||
defp object(%__MODULE__{} = draft) do
|
||||
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
||||
|
||||
# Sometimes people create posts with subject containing emoji,
|
||||
|
|
@ -325,15 +329,15 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|> Map.put("generator", draft.params[:generator])
|
||||
|> Map.put("language", draft.language)
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
%{draft | object: object}
|
||||
end
|
||||
|
||||
defp preview?(draft) do
|
||||
defp preview?(%__MODULE__{} = draft) do
|
||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||
%__MODULE__{draft | preview?: preview?}
|
||||
%{draft | preview?: preview?}
|
||||
end
|
||||
|
||||
defp changes(draft) do
|
||||
defp changes(%__MODULE__{} = draft) do
|
||||
direct? = draft.visibility == "direct"
|
||||
additional = %{"cc" => draft.cc, "directMessage" => direct?}
|
||||
|
||||
|
|
@ -353,14 +357,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
}
|
||||
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
|
||||
|
||||
%__MODULE__{draft | changes: changes}
|
||||
%{draft | changes: changes}
|
||||
end
|
||||
|
||||
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
|
||||
defp with_valid(draft, _func), do: draft
|
||||
|
||||
defp add_error(draft, message) do
|
||||
%__MODULE__{draft | valid?: false, errors: [message | draft.errors]}
|
||||
defp add_error(%__MODULE__{} = draft, message) do
|
||||
%{draft | valid?: false, errors: [message | draft.errors]}
|
||||
end
|
||||
|
||||
defp validate(%{valid?: true} = draft), do: {:ok, draft}
|
||||
|
|
|
|||
|
|
@ -75,48 +75,70 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
{Enum.map(participation.recipients, & &1.ap_id), []}
|
||||
end
|
||||
|
||||
def get_to_and_cc(%{visibility: visibility} = draft) when visibility in ["public", "local"] do
|
||||
to =
|
||||
case visibility do
|
||||
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
|
||||
"local" -> [Utils.as_local_public() | draft.mentions]
|
||||
def get_to_and_cc(%{visibility: visibility} = draft) do
|
||||
# If the OP is a DM already, add the implicit actor
|
||||
mentions =
|
||||
if visibility == "direct" && draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
|
||||
Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions])
|
||||
else
|
||||
draft.mentions
|
||||
end
|
||||
|
||||
cc = [draft.user.follower_address]
|
||||
|
||||
if draft.in_reply_to do
|
||||
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
|
||||
else
|
||||
{to, cc}
|
||||
end
|
||||
get_to_and_cc_for_visibility(
|
||||
visibility,
|
||||
draft.user.follower_address,
|
||||
draft.in_reply_to && draft.in_reply_to.data["actor"],
|
||||
mentions
|
||||
)
|
||||
end
|
||||
|
||||
def get_to_and_cc(%{visibility: "unlisted"} = draft) do
|
||||
to = [draft.user.follower_address | draft.mentions]
|
||||
cc = [Pleroma.Constants.as_public()]
|
||||
def get_to_and_cc_for_visibility("public", follower_collection, parent_actor, mentions) do
|
||||
scope_addr = Pleroma.Constants.as_public()
|
||||
|
||||
if draft.in_reply_to do
|
||||
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
|
||||
else
|
||||
{to, cc}
|
||||
end
|
||||
to =
|
||||
if parent_actor,
|
||||
do: Enum.uniq([parent_actor, scope_addr | mentions]),
|
||||
else: [scope_addr | mentions]
|
||||
|
||||
{to, [follower_collection]}
|
||||
end
|
||||
|
||||
def get_to_and_cc(%{visibility: "private"} = draft) do
|
||||
{to, cc} = get_to_and_cc(struct(draft, visibility: "direct"))
|
||||
{[draft.user.follower_address | to], cc}
|
||||
def get_to_and_cc_for_visibility("local", follower_collection, parent_actor, mentions) do
|
||||
recipients =
|
||||
if parent_actor,
|
||||
do: Enum.uniq([parent_actor | mentions]),
|
||||
else: mentions
|
||||
|
||||
to = [
|
||||
Utils.as_local_public()
|
||||
| Enum.filter(recipients, fn addr ->
|
||||
String.starts_with?(addr, Pleroma.Web.Endpoint.url() <> "/")
|
||||
end)
|
||||
]
|
||||
|
||||
{to, [follower_collection]}
|
||||
end
|
||||
|
||||
def get_to_and_cc(%{visibility: "direct"} = draft) do
|
||||
# If the OP is a DM already, add the implicit actor.
|
||||
if draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
|
||||
{Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions]), []}
|
||||
else
|
||||
{draft.mentions, []}
|
||||
end
|
||||
def get_to_and_cc_for_visibility("unlisted", follower_collection, parent_actor, mentions) do
|
||||
to =
|
||||
if parent_actor,
|
||||
do: Enum.uniq([parent_actor, follower_collection | mentions]),
|
||||
else: [follower_collection | mentions]
|
||||
|
||||
{to, [Pleroma.Constants.as_public()]}
|
||||
end
|
||||
|
||||
def get_to_and_cc(%{visibility: {:list, _}, mentions: mentions}), do: {mentions, []}
|
||||
def get_to_and_cc_for_visibility("private", follower_collection, _, mentions) do
|
||||
{[follower_collection | mentions], []}
|
||||
end
|
||||
|
||||
def get_to_and_cc_for_visibility("direct", _, _, mentions) do
|
||||
{mentions, []}
|
||||
end
|
||||
|
||||
def get_to_and_cc_for_visibility({:list, _}, _, _, mentions) do
|
||||
{mentions, []}
|
||||
end
|
||||
|
||||
def get_addressed_users(_, to) when is_list(to) do
|
||||
User.get_ap_ids_by_nicknames(to)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule Pleroma.Web.EmbedController do
|
|||
conn
|
||||
|> delete_resp_header("x-frame-options")
|
||||
|> delete_resp_header("content-security-policy")
|
||||
|> put_layout({Pleroma.Web.LayoutView, :embed})
|
||||
|> render("show.html",
|
||||
activity: activity,
|
||||
author: User.sanitize_html(author),
|
||||
|
|
|
|||
|
|
@ -29,9 +29,18 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
)
|
||||
end
|
||||
|
||||
def live_dashboard(conn, _params) do
|
||||
def live_dashboard(conn, %{"path" => path}) do
|
||||
query_params = conn.query_string
|
||||
|
||||
redirect_path =
|
||||
if query_params == "" do
|
||||
"/pleroma/live_dashboard/#{path}"
|
||||
else
|
||||
"/pleroma/live_dashboard/#{path}?#{query_params}"
|
||||
end
|
||||
|
||||
conn
|
||||
|> redirect(to: "/pleroma/live_dashboard")
|
||||
|> redirect(to: redirect_path)
|
||||
end
|
||||
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Web.OAuth.OAuthController
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.Web.Registration
|
||||
alias Pleroma.Web.Utils.Params
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||
|
|
@ -111,8 +111,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
_params
|
||||
) do
|
||||
with :ok <- validate_email_param(params),
|
||||
:ok <- TwitterAPI.validate_captcha(app, params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params),
|
||||
:ok <- Registration.validate_captcha(app, params),
|
||||
{:ok, user} <- Registration.register_user(params),
|
||||
{_, {:ok, token}} <-
|
||||
{:login, OAuthController.login(user, app, app.scopes)} do
|
||||
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.Web.Registration
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
def password_reset(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
TwitterAPI.password_reset(nickname_or_email)
|
||||
Registration.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,27 +28,27 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
|||
|
||||
# POST /api/v1/lists
|
||||
def create(
|
||||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: %{title: title}}}} =
|
||||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: params}}} =
|
||||
conn,
|
||||
_
|
||||
) do
|
||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(params, user) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
end
|
||||
|
||||
# GET /api/v1/lists/:idOB
|
||||
# GET /api/v1/lists/:id
|
||||
def show(%{assigns: %{list: list}} = conn, _) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
|
||||
# PUT /api/v1/lists/:id
|
||||
def update(
|
||||
%{assigns: %{list: list}, private: %{open_api_spex: %{body_params: %{title: title}}}} =
|
||||
%{assigns: %{list: list}, private: %{open_api_spex: %{body_params: params}}} =
|
||||
conn,
|
||||
_
|
||||
) do
|
||||
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
||||
with {:ok, list} <- Pleroma.List.update(list, params) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -81,10 +81,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin])
|
||||
|
||||
# Note: scope not present in Mastodon: read:bookmarks
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks)
|
||||
|
||||
# Note: scope not present in Mastodon: write:bookmarks
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> User.followed_hashtags()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
excluded_list_members =
|
||||
user
|
||||
|> Pleroma.List.get_exclusive_list_members()
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.put(:type, ["Create", "Announce"])
|
||||
|
|
@ -58,7 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> Map.delete(:local)
|
||||
|
||||
activities =
|
||||
[user.ap_id | User.following(user)]
|
||||
[user.ap_id | User.following(user) -- excluded_list_members]
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.ListView do
|
|||
def render("show.json", %{list: list}) do
|
||||
%{
|
||||
id: to_string(list.id),
|
||||
title: list.title
|
||||
title: list.title,
|
||||
exclusive: list.exclusive
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
||||
defmodule Pleroma.Web.OAuth.PasswordController do
|
||||
@moduledoc """
|
||||
The module contains functions for password reset.
|
||||
"""
|
||||
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
|||
alias Pleroma.PasswordResetToken
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.Web.Registration
|
||||
|
||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
|||
def request(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
TwitterAPI.password_reset(nickname_or_email)
|
||||
Registration.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.PasswordView do
|
||||
defmodule Pleroma.Web.OAuth.PasswordView do
|
||||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML.Form
|
||||
alias Pleroma.Web.Gettext
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
defmodule Pleroma.Web.OAuth.TokenController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.OAuth.TokenView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.TwitterAPI.TokenView
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.TokenView do
|
||||
defmodule Pleroma.Web.OAuth.TokenView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
def render("index.json", %{tokens: tokens}) do
|
||||
tokens
|
||||
|> render_many(Pleroma.Web.TwitterAPI.TokenView, "show.json")
|
||||
|> render_many(Pleroma.Web.OAuth.TokenView, "show.json")
|
||||
|> Enum.filter(&Enum.any?/1)
|
||||
end
|
||||
|
||||
|
|
@ -10,10 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
# Note: scope not present in Mastodon: read:bookmarks
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :index)
|
||||
|
||||
# Note: scope not present in Mastodon: write:bookmarks
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:bookmarks"]} when action in [:create, :update, :delete]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.PasswordController do
|
||||
@moduledoc """
|
||||
The module contains functions for password reset.
|
||||
"""
|
||||
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
require Logger
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
alias Pleroma.PasswordResetToken
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Registration
|
||||
|
||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||
|
||||
@doc "POST /auth/password"
|
||||
def request(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
Registration.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
||||
def reset(conn, %{"token" => token}) do
|
||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||
false <- PasswordResetToken.expired?(token),
|
||||
%User{} = user <- User.get_cached_by_id(token.user_id) do
|
||||
render(conn, "reset.html", %{
|
||||
token: token,
|
||||
user: user
|
||||
})
|
||||
else
|
||||
_e -> render(conn, "invalid_token.html")
|
||||
end
|
||||
end
|
||||
|
||||
def do_reset(conn, %{"data" => data}) do
|
||||
with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
|
||||
render(conn, "reset_success.html")
|
||||
else
|
||||
_e -> render(conn, "reset_failed.html")
|
||||
end
|
||||
end
|
||||
end
|
||||
59
lib/pleroma/web/pleroma_api/controllers/token_controller.ex
Normal file
59
lib/pleroma/web/pleroma_api/controllers/token_controller.ex
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.TokenController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.PleromaAPI.TokenView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
require Logger
|
||||
|
||||
plug(:skip_auth when action == :confirm_email)
|
||||
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||
with %User{} = user <- User.get_cached_by_id(uid),
|
||||
true <- user.local and !user.is_confirmed and user.confirmation_token == token,
|
||||
{:ok, _} <- User.confirm(user) do
|
||||
redirect(conn, to: "/")
|
||||
end
|
||||
end
|
||||
|
||||
def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
|
||||
with oauth_tokens <- Token.get_user_tokens(user) do
|
||||
conn
|
||||
|> put_view(TokenView)
|
||||
|> render("index.json", %{tokens: oauth_tokens})
|
||||
end
|
||||
end
|
||||
|
||||
def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
|
||||
Token.delete_user_token(user, id)
|
||||
|
||||
json_reply(conn, 201, "")
|
||||
end
|
||||
|
||||
defp errors(conn, {:param_cast, _}) do
|
||||
conn
|
||||
|> put_status(400)
|
||||
|> json("Invalid parameters")
|
||||
end
|
||||
|
||||
defp errors(conn, _) do
|
||||
conn
|
||||
|> put_status(500)
|
||||
|> json("Something went wrong")
|
||||
end
|
||||
|
||||
defp json_reply(conn, status, json) do
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(status, json)
|
||||
end
|
||||
end
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||
defmodule Pleroma.Web.PleromaAPI.UtilController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Healthcheck
|
||||
|
|
@ -17,19 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
plug(
|
||||
Pleroma.Web.ApiSpec.CastAndValidate,
|
||||
[replace_params: false]
|
||||
when action != :remote_subscribe and action != :show_subscribe_form
|
||||
)
|
||||
|
||||
plug(
|
||||
Pleroma.Web.Plugs.FederatingPlug
|
||||
when action == :remote_subscribe
|
||||
when action == :show_subscribe_form
|
||||
)
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
@ -54,125 +42,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
]
|
||||
)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation
|
||||
|
||||
def show_subscribe_form(conn, %{"nickname" => nick}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nick),
|
||||
avatar = User.avatar_url(user) do
|
||||
conn
|
||||
|> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
|
||||
else
|
||||
_e ->
|
||||
render(conn, "subscribe.html", %{
|
||||
nickname: nick,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"remote follow error message - user not found",
|
||||
"Could not find user"
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def show_subscribe_form(conn, %{"status_id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, ap_id} <- get_ap_id(activity),
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.actor),
|
||||
avatar = User.avatar_url(user) do
|
||||
conn
|
||||
|> render("status_interact.html", %{
|
||||
status_link: ap_id,
|
||||
status_id: id,
|
||||
nickname: user.nickname,
|
||||
avatar: avatar,
|
||||
error: false
|
||||
})
|
||||
else
|
||||
_e ->
|
||||
render(conn, "status_interact.html", %{
|
||||
status_id: id,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"status interact error message - status not found",
|
||||
"Could not find status"
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
||||
show_subscribe_form(conn, %{"nickname" => nick})
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
|
||||
show_subscribe_form(conn, %{"status_id" => id})
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||
else
|
||||
_e ->
|
||||
render(conn, "subscribe.html", %{
|
||||
nickname: nick,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"remote follow error message - unknown error",
|
||||
"Something went wrong."
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||
%Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, ap_id} <- get_ap_id(activity) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||
else
|
||||
_e ->
|
||||
render(conn, "status_interact.html", %{
|
||||
status_id: id,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"status interact error message - unknown error",
|
||||
"Something went wrong."
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_interaction(
|
||||
%{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
|
||||
_params
|
||||
) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
||||
conn
|
||||
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
||||
else
|
||||
_e -> json(conn, %{error: "Couldn't find user"})
|
||||
end
|
||||
end
|
||||
|
||||
defp get_ap_id(activity) do
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
|
||||
case object do
|
||||
%{data: %{"id" => ap_id}} -> {:ok, ap_id}
|
||||
_ -> {:no_ap_id, nil}
|
||||
end
|
||||
end
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaUtilOperation
|
||||
|
||||
def frontend_configurations(conn, _params) do
|
||||
render(conn, "frontend_configurations.json")
|
||||
|
|
@ -9,11 +9,13 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
|
|||
alias Pleroma.Emoji
|
||||
|
||||
def render("show.json", %{folder: %BookmarkFolder{} = folder}) do
|
||||
{emoji, emoji_url} = get_emoji(folder.emoji)
|
||||
|
||||
%{
|
||||
id: folder.id |> to_string(),
|
||||
name: folder.name,
|
||||
emoji: folder.emoji,
|
||||
emoji_url: get_emoji_url(folder.emoji)
|
||||
emoji: emoji,
|
||||
emoji_url: emoji_url
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -21,20 +23,15 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
|
|||
render_many(folders, __MODULE__, "show.json", Map.delete(opts, :folders))
|
||||
end
|
||||
|
||||
defp get_emoji_url(nil) do
|
||||
nil
|
||||
end
|
||||
defp get_emoji(nil), do: {nil, nil}
|
||||
|
||||
defp get_emoji_url(emoji) do
|
||||
defp get_emoji(emoji) do
|
||||
if Emoji.unicode?(emoji) do
|
||||
nil
|
||||
{emoji, nil}
|
||||
else
|
||||
emoji = Emoji.get(emoji)
|
||||
|
||||
if emoji != nil do
|
||||
Emoji.local_url(emoji.file)
|
||||
else
|
||||
nil
|
||||
case Emoji.get(emoji) do
|
||||
nil -> {nil, nil}
|
||||
emoji_data -> {emoji, Emoji.local_url(emoji_data.file)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
13
lib/pleroma/web/pleroma_api/views/util_view.ex
Normal file
13
lib/pleroma/web/pleroma_api/views/util_view.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.UtilView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Config
|
||||
|
||||
def render("frontend_configurations.json", _) do
|
||||
Config.get(:frontend_configurations, %{})
|
||||
|> Enum.into(%{})
|
||||
end
|
||||
end
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||
require Pleroma.Constants
|
||||
import Plug.Conn, only: [put_resp_header: 3]
|
||||
import Plug.Conn, only: [put_resp_header: 3, put_status: 2, send_resp: 3, halt: 1]
|
||||
|
||||
@moduledoc """
|
||||
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
||||
|
|
@ -51,10 +51,37 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|
|||
|> Map.put(:from, from)
|
||||
|> Map.put(:content_types, false)
|
||||
|
||||
conn = set_content_type(conn, conn.request_path)
|
||||
conn =
|
||||
conn
|
||||
|> set_content_type(conn.request_path)
|
||||
|> Plug.Static.call(opts)
|
||||
|
||||
# Call Plug.Static with our sanitized content-type
|
||||
Plug.Static.call(conn, opts)
|
||||
if conn.halted do
|
||||
conn
|
||||
else
|
||||
path = String.trim_leading(conn.request_path, "/")
|
||||
|
||||
if not File.exists?(file_path(path)) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> send_404()
|
||||
|> halt()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp send_404(conn) do
|
||||
if String.ends_with?(String.downcase(conn.request_path), ".json") do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/json")
|
||||
|> send_resp(404, Jason.encode!(%{error: "not found"}))
|
||||
else
|
||||
conn
|
||||
|> put_resp_header("content-type", "text/plain")
|
||||
|> send_resp(404, "Not found")
|
||||
end
|
||||
end
|
||||
|
||||
defp set_content_type(conn, "/emoji/" <> filepath) do
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Config.Holder
|
||||
alias Pleroma.EctoType.Config.RateLimit
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor
|
||||
|
||||
|
|
@ -143,7 +145,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
|
||||
def action_settings(plug_opts) do
|
||||
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
|
||||
limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do
|
||||
{:ok, limits} <- fetch_and_normalize_limits(limiter_name) do
|
||||
bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name)
|
||||
|
||||
%{
|
||||
|
|
@ -151,6 +153,72 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
limits: limits,
|
||||
opts: plug_opts
|
||||
}
|
||||
else
|
||||
:disabled -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_and_normalize_limits(limiter_name) do
|
||||
limits = Config.get([:rate_limit, limiter_name])
|
||||
|
||||
case normalize_limits(limits) do
|
||||
{:ok, limits} ->
|
||||
{:ok, limits}
|
||||
|
||||
:disabled ->
|
||||
:disabled
|
||||
|
||||
:error ->
|
||||
default_limits =
|
||||
Holder.default_config(:pleroma, :rate_limit)
|
||||
|> get_default_limits(limiter_name)
|
||||
|
||||
case normalize_limits(default_limits) do
|
||||
{:ok, normalized_limits} ->
|
||||
warn_invalid_limits_once(limiter_name, limits)
|
||||
{:ok, normalized_limits}
|
||||
|
||||
_ ->
|
||||
warn_invalid_limits_once(limiter_name, limits)
|
||||
:disabled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_default_limits(%{} = rate_limit, limiter_name), do: Map.get(rate_limit, limiter_name)
|
||||
|
||||
defp get_default_limits(rate_limit, limiter_name) when is_list(rate_limit) do
|
||||
if Keyword.keyword?(rate_limit) do
|
||||
Keyword.get(rate_limit, limiter_name)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp get_default_limits(_, _), do: nil
|
||||
|
||||
@invalid_limits_warned_key {__MODULE__, :invalid_limits_warned}
|
||||
|
||||
defp warn_invalid_limits_once(limiter_name, limits) do
|
||||
warned = :persistent_term.get(@invalid_limits_warned_key, MapSet.new())
|
||||
|
||||
if MapSet.member?(warned, limiter_name) do
|
||||
:ok
|
||||
else
|
||||
:persistent_term.put(@invalid_limits_warned_key, MapSet.put(warned, limiter_name))
|
||||
|
||||
Logger.warning(
|
||||
"Invalid rate limiter config for #{inspect(limiter_name)}: #{inspect(limits)}. Falling back to defaults or disabling this limiter."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_limits(nil), do: :disabled
|
||||
|
||||
defp normalize_limits(limits) do
|
||||
case RateLimit.cast(limits) do
|
||||
{:ok, normalized_limits} -> {:ok, normalized_limits}
|
||||
:error -> :error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
defmodule Pleroma.Web.Preload.Providers.Instance do
|
||||
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||
alias Pleroma.Web.Nodeinfo.Nodeinfo
|
||||
alias Pleroma.Web.PleromaAPI.UtilView
|
||||
alias Pleroma.Web.Plugs.InstanceStatic
|
||||
alias Pleroma.Web.Preload.Providers.Provider
|
||||
alias Pleroma.Web.TwitterAPI.UtilView
|
||||
|
||||
@behaviour Provider
|
||||
@instance_url "/api/v1/instance"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
defmodule Pleroma.Web.Registration do
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
alias Pleroma.Emails.Mailer
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||
defmodule Pleroma.Web.RemoteInteraction.RemoteInteractionController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
require Logger
|
||||
|
|
@ -14,18 +14,27 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
alias Pleroma.Web.Auth.TOTPAuthenticator
|
||||
alias Pleroma.Web.Auth.WrapperAuthenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
||||
|
||||
plug(
|
||||
Pleroma.Web.ApiSpec.CastAndValidate,
|
||||
[replace_params: false]
|
||||
when action == :remote_interaction
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.Plugs.FederatingPlug)
|
||||
|
||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
||||
plug(
|
||||
Pleroma.Web.Plugs.OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
||||
when action in [:do_follow]
|
||||
when action == :do_follow
|
||||
)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.RemoteInteractionOperation
|
||||
|
||||
# GET /ostatus_subscribe
|
||||
#
|
||||
def follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
|
||||
|
|
@ -125,7 +134,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
#
|
||||
def authorize_interaction(conn, %{"uri" => uri}) do
|
||||
conn
|
||||
|> redirect(to: Routes.remote_follow_path(conn, :follow, %{acct: uri}))
|
||||
|> redirect(to: Routes.remote_interaction_path(conn, :follow, %{acct: uri}))
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
|
||||
|
|
@ -162,4 +171,122 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||
end
|
||||
|
||||
def show_subscribe_form(conn, %{"nickname" => nick}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nick),
|
||||
avatar = User.avatar_url(user) do
|
||||
conn
|
||||
|> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
|
||||
else
|
||||
_e ->
|
||||
render(conn, "subscribe.html", %{
|
||||
nickname: nick,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"remote follow error message - user not found",
|
||||
"Could not find user"
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def show_subscribe_form(conn, %{"status_id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, ap_id} <- get_ap_id(activity),
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.actor),
|
||||
avatar = User.avatar_url(user) do
|
||||
conn
|
||||
|> render("status_interact.html", %{
|
||||
status_link: ap_id,
|
||||
status_id: id,
|
||||
nickname: user.nickname,
|
||||
avatar: avatar,
|
||||
error: false
|
||||
})
|
||||
else
|
||||
_e ->
|
||||
render(conn, "status_interact.html", %{
|
||||
status_id: id,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"status interact error message - status not found",
|
||||
"Could not find status"
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
||||
show_subscribe_form(conn, %{"nickname" => nick})
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
|
||||
show_subscribe_form(conn, %{"status_id" => id})
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||
else
|
||||
_e ->
|
||||
render(conn, "subscribe.html", %{
|
||||
nickname: nick,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"remote follow error message - unknown error",
|
||||
"Something went wrong."
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||
%Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, ap_id} <- get_ap_id(activity) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||
else
|
||||
_e ->
|
||||
render(conn, "status_interact.html", %{
|
||||
status_id: id,
|
||||
avatar: nil,
|
||||
error:
|
||||
Pleroma.Web.Gettext.dpgettext(
|
||||
"static_pages",
|
||||
"status interact error message - unknown error",
|
||||
"Something went wrong."
|
||||
)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def remote_interaction(
|
||||
%{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
|
||||
_params
|
||||
) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
||||
conn
|
||||
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
||||
else
|
||||
_e -> json(conn, %{error: "Couldn't find user"})
|
||||
end
|
||||
end
|
||||
|
||||
defp get_ap_id(activity) do
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
|
||||
case object do
|
||||
%{data: %{"id" => ap_id}} -> {:ok, ap_id}
|
||||
_ -> {:no_ap_id, nil}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do
|
||||
defmodule Pleroma.Web.RemoteInteraction.RemoteInteractionView do
|
||||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML
|
||||
import Phoenix.HTML.Form
|
||||
import Phoenix.HTML.Link
|
||||
alias Pleroma.Web.Gettext
|
||||
|
||||
def avatar_url(user) do
|
||||
|
|
@ -226,23 +226,28 @@ defmodule Pleroma.Web.Router do
|
|||
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do
|
||||
scope "/api/v1/pleroma", Pleroma.Web.OAuth do
|
||||
pipe_through(:pleroma_api)
|
||||
|
||||
get("/password_reset/:token", PasswordController, :reset, as: :reset_password)
|
||||
post("/password_reset", PasswordController, :do_reset, as: :reset_password)
|
||||
get("/emoji", UtilController, :emoji)
|
||||
get("/captcha", UtilController, :captcha)
|
||||
get("/healthcheck", UtilController, :healthcheck)
|
||||
post("/remote_interaction", UtilController, :remote_interaction)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:pleroma_api)
|
||||
|
||||
get("/emoji", UtilController, :emoji)
|
||||
get("/captcha", UtilController, :captcha)
|
||||
get("/healthcheck", UtilController, :healthcheck)
|
||||
get("/federation_status", InstancesController, :show)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web.RemoteInteraction do
|
||||
pipe_through(:pleroma_api)
|
||||
|
||||
post("/remote_interaction", RemoteInteractionController, :remote_interaction)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web do
|
||||
pipe_through(:pleroma_api)
|
||||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||
|
|
@ -484,18 +489,18 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.TwitterAPI do
|
||||
scope "/", Pleroma.Web.RemoteInteraction do
|
||||
pipe_through(:pleroma_html)
|
||||
|
||||
post("/main/ostatus", UtilController, :remote_subscribe)
|
||||
get("/main/ostatus", UtilController, :show_subscribe_form)
|
||||
get("/ostatus_subscribe", RemoteFollowController, :follow)
|
||||
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
|
||||
post("/main/ostatus", RemoteInteractionController, :remote_subscribe)
|
||||
get("/main/ostatus", RemoteInteractionController, :show_subscribe_form)
|
||||
get("/ostatus_subscribe", RemoteInteractionController, :follow)
|
||||
post("/ostatus_subscribe", RemoteInteractionController, :do_follow)
|
||||
|
||||
get("/authorize_interaction", RemoteFollowController, :authorize_interaction)
|
||||
get("/authorize_interaction", RemoteInteractionController, :authorize_interaction)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
scope "/api/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
post("/change_email", UtilController, :change_email)
|
||||
|
|
@ -853,7 +858,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api", Pleroma.Web do
|
||||
pipe_through(:config)
|
||||
|
||||
get("/pleroma/frontend_configurations", TwitterAPI.UtilController, :frontend_configurations)
|
||||
get("/pleroma/frontend_configurations", PleromaAPI.UtilController, :frontend_configurations)
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web do
|
||||
|
|
@ -861,7 +866,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get(
|
||||
"/account/confirm_email/:user_id/:token",
|
||||
TwitterAPI.Controller,
|
||||
OAuth.TokenController,
|
||||
:confirm_email,
|
||||
as: :confirm_email
|
||||
)
|
||||
|
|
@ -873,11 +878,11 @@ defmodule Pleroma.Web.Router do
|
|||
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web, as: :authenticated_twitter_api do
|
||||
scope "/api", Pleroma.Web, as: :authenticated_pleroma_api do
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
|
||||
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
|
||||
get("/oauth_tokens", OAuth.TokenController, :oauth_tokens)
|
||||
delete("/oauth_tokens/:id", OAuth.TokenController, :revoke_token)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
|
|
@ -1026,7 +1031,9 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/", Pleroma.Web do
|
||||
pipe_through(:pleroma_html)
|
||||
|
||||
post("/auth/password", TwitterAPI.PasswordController, :request)
|
||||
post("/auth/password", OAuth.PasswordController, :request)
|
||||
|
||||
get("/embed/:id", EmbedController, :show)
|
||||
end
|
||||
|
||||
scope "/proxy/", Pleroma.Web do
|
||||
|
|
@ -1088,7 +1095,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
||||
match(:*, "/api/pleroma/*path", LegacyPleromaApiRerouterPlug, [])
|
||||
get("/api/*path", RedirectController, :api_not_implemented)
|
||||
get("/phoenix/live_dashboard", RedirectController, :live_dashboard)
|
||||
get("/phoenix/live_dashboard/*path", RedirectController, :live_dashboard)
|
||||
get("/*path", RedirectController, :redirector_with_preload)
|
||||
|
||||
options("/*path", RedirectController, :empty)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %></h2>
|
||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||
<p><%= @followee.nickname %></p>
|
||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %>
|
||||
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "user"], fn f -> %>
|
||||
<%= hidden_input f, :id, value: @followee.id %>
|
||||
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %>
|
||||
<% end %>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %></h2>
|
||||
<p><%= @followee.nickname %></p>
|
||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
|
||||
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
|
||||
<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %>
|
||||
<br>
|
||||
<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<h2><%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %></h2>
|
||||
<p><%= @followee.nickname %></p>
|
||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
|
||||
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
|
||||
<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %>
|
||||
<br>
|
||||
<%= hidden_input f, :id, value: @followee.id %>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<h2><%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %></h2>
|
||||
<% else %>
|
||||
<h2><%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %></h2>
|
||||
<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %>
|
||||
<%= form_for @conn, Routes.remote_interaction_path(@conn, :remote_subscribe), [as: "status"], fn f -> %>
|
||||
<%= hidden_input f, :status_id, value: @status_id %>
|
||||
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
||||
<%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %></h2>
|
||||
<% else %>
|
||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %></h2>
|
||||
<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
|
||||
<%= form_for @conn, Routes.remote_interaction_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
|
||||
<%= hidden_input f, :nickname, value: @nickname %>
|
||||
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
||||
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<h1><%= link instance_name(), to: "/" %></h1>
|
||||
|
||||
<h3>
|
||||
<form class="pull-right collapse" method="POST" action="<%= Helpers.util_path(@conn, :remote_subscribe) %>">
|
||||
<form class="pull-right collapse" method="POST" action="<%= Helpers.remote_interaction_path(@conn, :remote_subscribe) %>">
|
||||
<input type="hidden" name="nickname" value="<%= @user.nickname %>">
|
||||
<input type="hidden" name="profile" value="">
|
||||
<button type="submit" class="collapse"><%= Gettext.dpgettext("static_pages", "static fe profile page remote follow button", "Remote follow") %></button>
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.UtilView do
|
||||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML
|
||||
import Phoenix.HTML.Form
|
||||
import Phoenix.HTML.Link
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Gettext
|
||||
|
||||
def status_net_config(instance) do
|
||||
"""
|
||||
<config>
|
||||
<site>
|
||||
<name>#{Keyword.get(instance, :name)}</name>
|
||||
<site>#{Endpoint.url()}</site>
|
||||
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
||||
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
||||
</site>
|
||||
</config>
|
||||
"""
|
||||
end
|
||||
|
||||
def render("frontend_configurations.json", _) do
|
||||
Config.get(:frontend_configurations, %{})
|
||||
|> Enum.into(%{})
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue