Merge branch 'develop' into issue/2099
This commit is contained in:
commit
2ec0dcf001
62 changed files with 1421 additions and 159 deletions
|
|
@ -32,7 +32,8 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|
||||
@spec migrate_to_db(Path.t() | nil) :: any()
|
||||
def migrate_to_db(file_path \\ nil) do
|
||||
if Pleroma.Config.get([:configurable_from_database]) do
|
||||
with true <- Pleroma.Config.get([:configurable_from_database]),
|
||||
:ok <- Pleroma.Config.DeprecationWarnings.warn() do
|
||||
config_file =
|
||||
if file_path do
|
||||
file_path
|
||||
|
|
@ -46,7 +47,8 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|
||||
do_migrate_to_db(config_file)
|
||||
else
|
||||
migration_error()
|
||||
:error -> deprecation_error()
|
||||
_ -> migration_error()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -120,6 +122,10 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
)
|
||||
end
|
||||
|
||||
defp deprecation_error do
|
||||
shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
|
||||
end
|
||||
|
||||
if Code.ensure_loaded?(Config.Reader) do
|
||||
defp config_header, do: "import Config\r\n\r\n"
|
||||
defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ defmodule Pleroma.Chat do
|
|||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Chat
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
|
|
@ -69,4 +71,12 @@ defmodule Pleroma.Chat do
|
|||
conflict_target: [:user_id, :recipient]
|
||||
)
|
||||
end
|
||||
|
||||
@spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t()
|
||||
def for_user_query(user_id) do
|
||||
from(c in Chat,
|
||||
where: c.user_id == ^user_id,
|
||||
order_by: [desc: c.updated_at]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
!!!DEPRECATION WARNING!!!
|
||||
You are using the old configuration mechanism for the hellthread filter. Please check config.md.
|
||||
""")
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -47,17 +51,26 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
|
||||
config :pleroma, :mrf_user_allowlist, #{inspect(rewritten, pretty: true)}
|
||||
""")
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def warn do
|
||||
check_hellthread_threshold()
|
||||
mrf_user_allowlist()
|
||||
check_old_mrf_config()
|
||||
check_media_proxy_whitelist_config()
|
||||
check_welcome_message_config()
|
||||
check_gun_pool_options()
|
||||
check_activity_expiration_config()
|
||||
with :ok <- check_hellthread_threshold(),
|
||||
:ok <- mrf_user_allowlist(),
|
||||
:ok <- check_old_mrf_config(),
|
||||
:ok <- check_media_proxy_whitelist_config(),
|
||||
:ok <- check_welcome_message_config(),
|
||||
:ok <- check_gun_pool_options(),
|
||||
:ok <- check_activity_expiration_config() do
|
||||
:ok
|
||||
else
|
||||
_ ->
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def check_welcome_message_config do
|
||||
|
|
@ -74,6 +87,10 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
\n* `config :pleroma, :instance, welcome_user_nickname` is now `config :pleroma, :welcome, :direct_message, :sender_nickname`
|
||||
\n* `config :pleroma, :instance, welcome_message` is now `config :pleroma, :welcome, :direct_message, :message`
|
||||
""")
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -101,8 +118,11 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
end
|
||||
end)
|
||||
|
||||
if warning != "" do
|
||||
if warning == "" do
|
||||
:ok
|
||||
else
|
||||
Logger.warn(warning_preface <> warning)
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -115,6 +135,10 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old format (only domain) for MediaProxy whitelist option. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
|
||||
""")
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -157,6 +181,9 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
Logger.warn(Enum.join([warning_preface | pool_warnings]))
|
||||
|
||||
Config.put(:pools, updated_config)
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -320,6 +320,19 @@ defmodule Pleroma.ModerationLog do
|
|||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor.nickname},
|
||||
"action" => "chat_message_delete",
|
||||
"subject_id" => subject_id
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
|
||||
defp insert_log_entry_with_message(entry) do
|
||||
entry.data["message"]
|
||||
|
|
@ -627,6 +640,17 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "chat_message_delete",
|
||||
"subject_id" => subject_id
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted chat message ##{subject_id}"
|
||||
end
|
||||
|
||||
defp nicknames_to_string(nicknames) do
|
||||
nicknames
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ defmodule Pleroma.User.Search do
|
|||
|> base_query(following)
|
||||
|> filter_blocked_user(for_user)
|
||||
|> filter_invisible_users()
|
||||
|> filter_discoverable_users()
|
||||
|> filter_internal_users()
|
||||
|> filter_blocked_domains(for_user)
|
||||
|> fts_search(query_string)
|
||||
|
|
@ -122,6 +123,10 @@ defmodule Pleroma.User.Search do
|
|||
from(q in query, where: q.invisible == false)
|
||||
end
|
||||
|
||||
defp filter_discoverable_users(query) do
|
||||
from(q in query, where: q.discoverable == true)
|
||||
end
|
||||
|
||||
defp filter_internal_users(query) do
|
||||
from(q in query, where: q.actor_type != "Application")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,16 +5,34 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
|
||||
def filter(policies, %{} = object) do
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|> Enum.reduce({:ok, object}, fn
|
||||
policy, {:ok, object} -> policy.filter(object)
|
||||
|> Enum.reduce({:ok, message}, fn
|
||||
policy, {:ok, message} -> policy.filter(message)
|
||||
_, error -> error
|
||||
end)
|
||||
end
|
||||
|
||||
def filter(%{} = object), do: get_policies() |> filter(object)
|
||||
|
||||
def pipeline_filter(%{} = message, meta) do
|
||||
object = meta[:object_data]
|
||||
ap_id = message["object"]
|
||||
|
||||
if object && ap_id do
|
||||
with {:ok, message} <- filter(Map.put(message, "object", object)) do
|
||||
meta = Keyword.put(meta, :object_data, message["object"])
|
||||
{:ok, Map.put(message, "object", ap_id), meta}
|
||||
else
|
||||
{err, message} -> {err, message, meta}
|
||||
end
|
||||
else
|
||||
{err, message} = filter(message)
|
||||
|
||||
{err, message, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def get_policies do
|
||||
Pleroma.Config.get([:mrf, :policies], []) |> get_policies()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,9 +20,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
String.match?(string, pattern)
|
||||
end
|
||||
|
||||
defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||
defp object_payload(%{} = object) do
|
||||
[object["content"], object["summary"], object["name"]]
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
|
||||
defp check_reject(%{"object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
|
||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
{:reject, "[KeywordPolicy] Matches with rejected keyword"}
|
||||
else
|
||||
|
|
@ -30,12 +38,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_ftl_removal(
|
||||
%{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message
|
||||
) do
|
||||
defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
|
||||
if Pleroma.Constants.as_public() in to and
|
||||
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
to = List.delete(to, Pleroma.Constants.as_public())
|
||||
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
|
||||
|
|
@ -51,35 +59,24 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||
content =
|
||||
if is_binary(content) do
|
||||
content
|
||||
else
|
||||
""
|
||||
end
|
||||
defp check_replace(%{"object" => %{} = object} = message) do
|
||||
object =
|
||||
["content", "name", "summary"]
|
||||
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|
||||
|> Enum.reduce(object, fn field, object ->
|
||||
data =
|
||||
Enum.reduce(
|
||||
Pleroma.Config.get([:mrf_keyword, :replace]),
|
||||
object[field],
|
||||
fn {pat, repl}, acc -> String.replace(acc, pat, repl) end
|
||||
)
|
||||
|
||||
summary =
|
||||
if is_binary(summary) do
|
||||
summary
|
||||
else
|
||||
""
|
||||
end
|
||||
Map.put(object, field, data)
|
||||
end)
|
||||
|
||||
{content, summary} =
|
||||
Enum.reduce(
|
||||
Pleroma.Config.get([:mrf_keyword, :replace]),
|
||||
{content, summary},
|
||||
fn {pattern, replacement}, {content_acc, summary_acc} ->
|
||||
{String.replace(content_acc, pattern, replacement),
|
||||
String.replace(summary_acc, pattern, replacement)}
|
||||
end
|
||||
)
|
||||
message = Map.put(message, "object", object)
|
||||
|
||||
{:ok,
|
||||
message
|
||||
|> put_in(["object", "content"], content)
|
||||
|> put_in(["object", "summary"], summary)}
|
||||
{:ok, message}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
}"
|
||||
)
|
||||
|
||||
subchain
|
||||
|> MRF.filter(message)
|
||||
MRF.filter(subchain, message)
|
||||
else
|
||||
_e -> {:ok, message}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,13 +26,17 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
|||
|
||||
{:error, e} ->
|
||||
{:error, e}
|
||||
|
||||
{:reject, e} ->
|
||||
{:reject, e}
|
||||
end
|
||||
end
|
||||
|
||||
def do_common_pipeline(object, meta) do
|
||||
with {_, {:ok, validated_object, meta}} <-
|
||||
{:validate_object, ObjectValidator.validate(object, meta)},
|
||||
{_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
|
||||
{_, {:ok, mrfd_object, meta}} <-
|
||||
{:mrf_object, MRF.pipeline_filter(validated_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
{:persist_object, ActivityPub.persist(mrfd_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
|
|
@ -40,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
|||
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
|
||||
{:ok, activity, meta}
|
||||
else
|
||||
{:mrf_object, {:reject, _}} -> {:ok, nil, meta}
|
||||
{:mrf_object, {:reject, message, _}} -> {:reject, message}
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
require Logger
|
||||
|
||||
@users_page_size 50
|
||||
|
||||
plug(
|
||||
|
|
@ -68,6 +66,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
when action in [:list_user_statuses, :list_instance_statuses]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:chats"], admin: true}
|
||||
when action in [:list_user_chats]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"], admin: true}
|
||||
|
|
@ -256,6 +260,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do
|
||||
with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||
chats =
|
||||
Pleroma.Chat.for_user_query(user_id)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
conn
|
||||
|> put_view(AdminAPI.ChatView)
|
||||
|> render("index.json", chats: chats)
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
|
|
|
|||
85
lib/pleroma/web/admin_api/controllers/chat_controller.ex
Normal file
85
lib/pleroma/web/admin_api/controllers/chat_controller.ex
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.ChatController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Chat
|
||||
alias Pleroma.Chat.MessageReference
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
||||
|
||||
require Logger
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:chats"], admin: true} when action in [:show, :messages]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:chats"], admin: true} when action in [:delete_message]
|
||||
)
|
||||
|
||||
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ChatOperation
|
||||
|
||||
def delete_message(%{assigns: %{user: user}} = conn, %{
|
||||
message_id: message_id,
|
||||
id: chat_id
|
||||
}) do
|
||||
with %MessageReference{object: %{data: %{"id" => object_ap_id}}} = cm_ref <-
|
||||
MessageReference.get_by_id(message_id),
|
||||
^chat_id <- to_string(cm_ref.chat_id),
|
||||
%Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id),
|
||||
{:ok, _} <- CommonAPI.delete(activity_id, user) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "chat_message_delete",
|
||||
actor: user,
|
||||
subject_id: message_id
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(MessageReferenceView)
|
||||
|> render("show.json", chat_message_reference: cm_ref)
|
||||
else
|
||||
_e ->
|
||||
{:error, :could_not_delete}
|
||||
end
|
||||
end
|
||||
|
||||
def messages(conn, %{id: id} = params) do
|
||||
with %Chat{} = chat <- Chat.get_by_id(id) do
|
||||
cm_refs =
|
||||
chat
|
||||
|> MessageReference.for_chat_query()
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> put_view(MessageReferenceView)
|
||||
|> render("index.json", chat_message_references: cm_refs)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "not found"})
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, %{id: id}) do
|
||||
with %Chat{} = chat <- Chat.get_by_id(id) do
|
||||
conn
|
||||
|> put_view(AdminAPI.ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Plugs.InstanceStatic
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.InstanceDocument
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :show)
|
||||
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [:update, :delete])
|
||||
|
||||
def show(conn, %{name: document_name}) do
|
||||
with {:ok, url} <- InstanceDocument.get(document_name),
|
||||
{:ok, content} <- File.read(InstanceStatic.file_path(url)) do
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_resp(200, content)
|
||||
end
|
||||
end
|
||||
|
||||
def update(%{body_params: %{file: file}} = conn, %{name: document_name}) do
|
||||
with {:ok, url} <- InstanceDocument.put(document_name, file.path) do
|
||||
json(conn, %{"url" => url})
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{name: document_name}) do
|
||||
with :ok <- InstanceDocument.delete(document_name) do
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
end
|
||||
30
lib/pleroma/web/admin_api/views/chat_view.ex
Normal file
30
lib/pleroma/web/admin_api/views/chat_view.ex
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.ChatView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Chat
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MastodonAPI
|
||||
alias Pleroma.Web.PleromaAPI
|
||||
|
||||
def render("index.json", %{chats: chats} = opts) do
|
||||
render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats))
|
||||
end
|
||||
|
||||
def render("show.json", %{chat: %Chat{user_id: user_id}} = opts) do
|
||||
user = User.get_by_id(user_id)
|
||||
sender = MastodonAPI.AccountView.render("show.json", user: user, skip_visibility_check: true)
|
||||
|
||||
serialized_chat = PleromaAPI.ChatView.render("show.json", opts)
|
||||
|
||||
serialized_chat
|
||||
|> Map.put(:sender, sender)
|
||||
|> Map.put(:receiver, serialized_chat[:account])
|
||||
|> Map.delete(:account)
|
||||
end
|
||||
|
||||
def render(view, opts), do: PleromaAPI.ChatView.render(view, opts)
|
||||
end
|
||||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
|||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI
|
||||
|
||||
defdelegate merge_account_views(user), to: AdminAPI.AccountView
|
||||
|
|
@ -17,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
|||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
user = MastodonAPI.StatusView.get_user(activity.data["actor"])
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
|
||||
MastodonAPI.StatusView.render("show.json", opts)
|
||||
|> Map.merge(%{account: merge_account_views(user)})
|
||||
|
|
|
|||
96
lib/pleroma/web/api_spec/operations/admin/chat_operation.ex
Normal file
96
lib/pleroma/web/api_spec/operations/admin/chat_operation.ex
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Chat
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def delete_message_operation do
|
||||
%Operation{
|
||||
tags: ["admin", "chat"],
|
||||
summary: "Delete an individual chat message",
|
||||
operationId: "AdminAPI.ChatController.delete_message",
|
||||
parameters: [
|
||||
Operation.parameter(:id, :path, :string, "The ID of the Chat"),
|
||||
Operation.parameter(:message_id, :path, :string, "The ID of the message")
|
||||
],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"The deleted ChatMessage",
|
||||
"application/json",
|
||||
ChatMessage
|
||||
)
|
||||
},
|
||||
security: [
|
||||
%{
|
||||
"oAuth" => ["write:chats"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def messages_operation do
|
||||
%Operation{
|
||||
tags: ["admin", "chat"],
|
||||
summary: "Get the most recent messages of the chat",
|
||||
operationId: "AdminAPI.ChatController.messages",
|
||||
parameters:
|
||||
[Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++
|
||||
pagination_params(),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"The messages in the chat",
|
||||
"application/json",
|
||||
Pleroma.Web.ApiSpec.ChatOperation.chat_messages_response()
|
||||
)
|
||||
},
|
||||
security: [
|
||||
%{
|
||||
"oAuth" => ["read:chats"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["chat"],
|
||||
summary: "Create a chat",
|
||||
operationId: "AdminAPI.ChatController.show",
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:id,
|
||||
:path,
|
||||
:string,
|
||||
"The id of the chat",
|
||||
required: true,
|
||||
example: "1234"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"The existing chat",
|
||||
"application/json",
|
||||
Chat
|
||||
)
|
||||
},
|
||||
security: [
|
||||
%{
|
||||
"oAuth" => ["read"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Helpers
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["Admin", "InstanceDocument"],
|
||||
summary: "Get the instance document",
|
||||
operationId: "AdminAPI.InstanceDocumentController.show",
|
||||
security: [%{"oAuth" => ["read"]}],
|
||||
parameters: [
|
||||
Operation.parameter(:name, :path, %Schema{type: :string}, "The document name",
|
||||
required: true
|
||||
)
|
||||
| Helpers.admin_api_params()
|
||||
],
|
||||
responses: %{
|
||||
200 => document_content(),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_operation do
|
||||
%Operation{
|
||||
tags: ["Admin", "InstanceDocument"],
|
||||
summary: "Update the instance document",
|
||||
operationId: "AdminAPI.InstanceDocumentController.update",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
requestBody: Helpers.request_body("Parameters", update_request()),
|
||||
parameters: [
|
||||
Operation.parameter(:name, :path, %Schema{type: :string}, "The document name",
|
||||
required: true
|
||||
)
|
||||
| Helpers.admin_api_params()
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("InstanceDocument", "application/json", instance_document()),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp update_request do
|
||||
%Schema{
|
||||
title: "UpdateRequest",
|
||||
description: "POST body for uploading the file",
|
||||
type: :object,
|
||||
required: [:file],
|
||||
properties: %{
|
||||
file: %Schema{
|
||||
type: :string,
|
||||
format: :binary,
|
||||
description: "The file to be uploaded, using multipart form data."
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def delete_operation do
|
||||
%Operation{
|
||||
tags: ["Admin", "InstanceDocument"],
|
||||
summary: "Get the instance document",
|
||||
operationId: "AdminAPI.InstanceDocumentController.delete",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
parameters: [
|
||||
Operation.parameter(:name, :path, %Schema{type: :string}, "The document name",
|
||||
required: true
|
||||
)
|
||||
| Helpers.admin_api_params()
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("InstanceDocument", "application/json", instance_document()),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp instance_document do
|
||||
%Schema{
|
||||
title: "InstanceDocument",
|
||||
type: :object,
|
||||
properties: %{
|
||||
url: %Schema{type: :string}
|
||||
},
|
||||
example: %{
|
||||
"url" => "https://example.com/static/terms-of-service.html"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp document_content do
|
||||
Operation.response("InstanceDocumentContent", "text/html", %Schema{
|
||||
type: :string,
|
||||
example: "<h1>Instance panel</h1>"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
@ -184,7 +184,8 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
|||
"application/json",
|
||||
ChatMessage
|
||||
),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
422 => Operation.response("MRF Rejection", "application/json", ApiError)
|
||||
},
|
||||
security: [
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
"application/json",
|
||||
%Schema{oneOf: [Status, ScheduledStatus]}
|
||||
),
|
||||
422 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
422 => Operation.response("Bad Request / MRF Rejection", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
local: true
|
||||
)} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:common_pipeline, {:reject, _} = e} -> e
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -550,4 +553,21 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def show_reblogs(%User{} = user, %User{} = target) do
|
||||
UserRelationship.delete_reblog_mute(user, target)
|
||||
end
|
||||
|
||||
def get_user(ap_id, fake_record_fallback \\ true) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
fake_record_fallback ->
|
||||
# TODO: refactor (fake records is never a good idea)
|
||||
User.error_user(ap_id)
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
62
lib/pleroma/web/instance_document.ex
Normal file
62
lib/pleroma/web/instance_document.ex
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.InstanceDocument do
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
@instance_documents %{
|
||||
"terms-of-service" => "/static/terms-of-service.html",
|
||||
"instance-panel" => "/instance/panel.html"
|
||||
}
|
||||
|
||||
@spec get(String.t()) :: {:ok, String.t()} | {:error, atom()}
|
||||
def get(document_name) do
|
||||
case Map.fetch(@instance_documents, document_name) do
|
||||
{:ok, path} -> {:ok, path}
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
@spec put(String.t(), String.t()) :: {:ok, String.t()} | {:error, atom()}
|
||||
def put(document_name, origin_path) do
|
||||
with {_, {:ok, destination_path}} <-
|
||||
{:instance_document, Map.fetch(@instance_documents, document_name)},
|
||||
:ok <- put_file(origin_path, destination_path) do
|
||||
{:ok, Path.join(Endpoint.url(), destination_path)}
|
||||
else
|
||||
{:instance_document, :error} -> {:error, :not_found}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@spec delete(String.t()) :: :ok | {:error, atom()}
|
||||
def delete(document_name) do
|
||||
with {_, {:ok, path}} <- {:instance_document, Map.fetch(@instance_documents, document_name)},
|
||||
instance_static_dir_path <- instance_static_dir(path),
|
||||
:ok <- File.rm(instance_static_dir_path) do
|
||||
:ok
|
||||
else
|
||||
{:instance_document, :error} -> {:error, :not_found}
|
||||
{:error, :enoent} -> {:error, :not_found}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp put_file(origin_path, destination_path) do
|
||||
with destination <- instance_static_dir(destination_path),
|
||||
{_, :ok} <- {:mkdir_p, File.mkdir_p(Path.dirname(destination))},
|
||||
{_, {:ok, _}} <- {:copy, File.copy(origin_path, destination)} do
|
||||
:ok
|
||||
else
|
||||
{error, _} -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp instance_static_dir(filename) do
|
||||
[:instance, :static_dir]
|
||||
|> Config.get!()
|
||||
|> Path.join(filename)
|
||||
end
|
||||
end
|
||||
|
|
@ -55,23 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end)
|
||||
end
|
||||
|
||||
def get_user(ap_id, fake_record_fallback \\ true) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
fake_record_fallback ->
|
||||
# TODO: refactor (fake records is never a good idea)
|
||||
User.error_user(ap_id)
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id),
|
||||
do: context_id
|
||||
|
||||
|
|
@ -119,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
# Note: unresolved users are filtered out
|
||||
actors =
|
||||
(activities ++ parent_activities)
|
||||
|> Enum.map(&get_user(&1.data["actor"], false))
|
||||
|> Enum.map(&CommonAPI.get_user(&1.data["actor"], false))
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
|
||||
|
|
@ -138,7 +121,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
"show.json",
|
||||
%{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts
|
||||
) do
|
||||
user = get_user(activity.data["actor"])
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
activity_object = Object.normalize(activity)
|
||||
|
||||
|
|
@ -211,7 +194,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
object = Object.normalize(activity)
|
||||
|
||||
user = get_user(activity.data["actor"])
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
user_follower_address = user.follower_address
|
||||
|
||||
like_count = object.data["like_count"] || 0
|
||||
|
|
@ -265,7 +248,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
reply_to = get_reply_to(activity, opts)
|
||||
|
||||
reply_to_user = reply_to && get_user(reply_to.data["actor"])
|
||||
reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"])
|
||||
|
||||
content =
|
||||
object
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do
|
|||
"""
|
||||
|
||||
@impl true
|
||||
def build_tags(%{user: %{local: false}}) do
|
||||
def build_tags(%{user: %{local: true, discoverable: true}}), do: []
|
||||
|
||||
def build_tags(_) do
|
||||
[
|
||||
{:meta,
|
||||
[
|
||||
|
|
@ -19,7 +21,4 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do
|
|||
], []}
|
||||
]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def build_tags(%{user: %{local: true}}), do: []
|
||||
end
|
||||
|
|
|
|||
|
|
@ -90,6 +90,16 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
conn
|
||||
|> put_view(MessageReferenceView)
|
||||
|> render("show.json", chat_message_reference: cm_ref)
|
||||
else
|
||||
{:reject, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -146,11 +156,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
blocked_ap_ids = User.blocked_users_ap_ids(user)
|
||||
|
||||
chats =
|
||||
from(c in Chat,
|
||||
where: c.user_id == ^user_id,
|
||||
where: c.recipient not in ^blocked_ap_ids,
|
||||
order_by: [desc: c.updated_at]
|
||||
)
|
||||
Chat.for_user_query(user_id)
|
||||
|> where([c], c.recipient not in ^blocked_ap_ids)
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
|
||||
object = Object.normalize(activity)
|
||||
|
||||
user = StatusView.get_user(activity.data["actor"])
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -20,36 +20,61 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
with {:ok, data} <- get_cached_or_parse(url),
|
||||
{:ok, _} <- set_ttl_based_on_image(data, url) do
|
||||
{:ok, data}
|
||||
else
|
||||
{:error, {:invalid_metadata, data}} = e ->
|
||||
Logger.debug(fn -> "Incomplete or invalid metadata for #{url}: #{inspect(data)}" end)
|
||||
e
|
||||
|
||||
error ->
|
||||
Logger.error(fn -> "Rich media error for #{url}: #{inspect(error)}" end)
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp get_cached_or_parse(url) do
|
||||
case Cachex.fetch!(:rich_media_cache, url, fn _ -> {:commit, parse_url(url)} end) do
|
||||
{:ok, _data} = res ->
|
||||
res
|
||||
case Cachex.fetch(:rich_media_cache, url, fn ->
|
||||
case parse_url(url) do
|
||||
{:ok, _} = res ->
|
||||
{:commit, res}
|
||||
|
||||
{:error, :body_too_large} = e ->
|
||||
e
|
||||
{:error, reason} = e ->
|
||||
# Unfortunately we have to log errors here, instead of doing that
|
||||
# along with ttl setting at the bottom. Otherwise we can get log spam
|
||||
# if more than one process was waiting for the rich media card
|
||||
# while it was generated. Ideally we would set ttl here as well,
|
||||
# so we don't override it number_of_waiters_on_generation
|
||||
# times, but one, obviously, can't set ttl for not-yet-created entry
|
||||
# and Cachex doesn't support returning ttl from the fetch callback.
|
||||
log_error(url, reason)
|
||||
{:commit, e}
|
||||
end
|
||||
end) do
|
||||
{action, res} when action in [:commit, :ok] ->
|
||||
case res do
|
||||
{:ok, _data} = res ->
|
||||
res
|
||||
|
||||
{:error, {:content_type, _}} = e ->
|
||||
e
|
||||
{:error, reason} = e ->
|
||||
if action == :commit, do: set_error_ttl(url, reason)
|
||||
e
|
||||
end
|
||||
|
||||
# The TTL is not set for the errors above, since they are unlikely to change
|
||||
# with time
|
||||
{:error, _} = e ->
|
||||
ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000)
|
||||
Cachex.expire(:rich_media_cache, url, ttl)
|
||||
e
|
||||
{:error, e} ->
|
||||
{:error, {:cachex_error, e}}
|
||||
end
|
||||
end
|
||||
|
||||
defp set_error_ttl(_url, :body_too_large), do: :ok
|
||||
defp set_error_ttl(_url, {:content_type, _}), do: :ok
|
||||
|
||||
# The TTL is not set for the errors above, since they are unlikely to change
|
||||
# with time
|
||||
|
||||
defp set_error_ttl(url, _reason) do
|
||||
ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000)
|
||||
Cachex.expire(:rich_media_cache, url, ttl)
|
||||
:ok
|
||||
end
|
||||
|
||||
defp log_error(url, {:invalid_metadata, data}) do
|
||||
Logger.debug(fn -> "Incomplete or invalid metadata for #{url}: #{inspect(data)}" end)
|
||||
end
|
||||
|
||||
defp log_error(url, reason) do
|
||||
Logger.warn(fn -> "Rich media error for #{url}: #{inspect(reason)}" end)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -178,9 +178,14 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users", AdminAPIController, :list_users)
|
||||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||
|
||||
get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
|
||||
|
||||
get("/instance_document/:name", InstanceDocumentController, :show)
|
||||
patch("/instance_document/:name", InstanceDocumentController, :update)
|
||||
delete("/instance_document/:name", InstanceDocumentController, :delete)
|
||||
|
||||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||
|
||||
|
|
@ -214,6 +219,10 @@ defmodule Pleroma.Web.Router do
|
|||
get("/media_proxy_caches", MediaProxyCacheController, :index)
|
||||
post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
|
||||
post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
|
||||
|
||||
get("/chats/:id", ChatController, :show)
|
||||
get("/chats/:id/messages", ChatController, :messages)
|
||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||
end
|
||||
|
||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue