Merge remote-tracking branch 'upstream/develop' into registration-workflow
This commit is contained in:
commit
9546c1444c
236 changed files with 5448 additions and 2802 deletions
|
|
@ -284,7 +284,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
defp upload_filters(filters) when is_map(filters) do
|
||||
enabled_filters =
|
||||
if filters.strip do
|
||||
[Pleroma.Upload.Filter.ExifTool]
|
||||
[Pleroma.Upload.Filter.Exiftool]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
|> Enum.each(fn user ->
|
||||
shell_info(
|
||||
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
|
||||
user.locked
|
||||
user.is_locked
|
||||
}, deactivated: #{user.deactivated}"
|
||||
)
|
||||
end)
|
||||
|
|
@ -447,10 +447,10 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
defp set_locked(user, value) do
|
||||
{:ok, user} =
|
||||
user
|
||||
|> Changeset.change(%{locked: value})
|
||||
|> Changeset.change(%{is_locked: value})
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
shell_info("Locked status of #{user.nickname}: #{user.locked}")
|
||||
shell_info("Locked status of #{user.nickname}: #{user.is_locked}")
|
||||
user
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,12 @@ defmodule Phoenix.Transports.WebSocket.Raw do
|
|||
|
||||
case conn do
|
||||
%{halted: false} = conn ->
|
||||
case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do
|
||||
case handler.connect(%{
|
||||
endpoint: endpoint,
|
||||
transport: transport,
|
||||
options: [serializer: nil],
|
||||
params: conn.params
|
||||
}) do
|
||||
{:ok, socket} ->
|
||||
{:ok, conn, {__MODULE__, {socket, opts}}}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.ReportNote
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
|
@ -153,6 +154,18 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def get_bookmark(_, _), do: nil
|
||||
|
||||
def get_report(activity_id) do
|
||||
opts = %{
|
||||
type: "Flag",
|
||||
skip_preload: true,
|
||||
preload_report_notes: true
|
||||
}
|
||||
|
||||
ActivityPub.fetch_activities_query([], opts)
|
||||
|> where(id: ^activity_id)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def change(struct, params \\ %{}) do
|
||||
struct
|
||||
|> cast(params, [:data, :recipients])
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ defmodule Pleroma.Activity.Ir.Topics do
|
|||
end
|
||||
|
||||
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
||||
tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||
tags ++
|
||||
remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||
end
|
||||
|
||||
defp item_creation_tags(tags, _, _) do
|
||||
|
|
@ -55,9 +56,19 @@ defmodule Pleroma.Activity.Ir.Topics do
|
|||
|
||||
defp hashtags_to_topics(_), do: []
|
||||
|
||||
defp remote_topics(%{local: true}), do: []
|
||||
|
||||
defp remote_topics(%{actor: actor}) when is_binary(actor),
|
||||
do: ["public:remote:" <> URI.parse(actor).host]
|
||||
|
||||
defp remote_topics(_), do: []
|
||||
|
||||
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
||||
|
||||
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
||||
|
||||
defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
|
||||
do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
|
||||
|
||||
defp attachment_topics(_object, _act), do: ["public:media"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -95,11 +95,12 @@ defmodule Pleroma.Application do
|
|||
[
|
||||
Pleroma.Stats,
|
||||
Pleroma.JobQueueMonitor,
|
||||
{Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]},
|
||||
{Oban, Config.get(Oban)}
|
||||
] ++
|
||||
task_children(@env) ++
|
||||
dont_run_in_test(@env) ++
|
||||
chat_child(@env, chat_enabled?()) ++
|
||||
chat_child(chat_enabled?()) ++
|
||||
[
|
||||
Pleroma.Web.Endpoint,
|
||||
Pleroma.Gopher.Server
|
||||
|
|
@ -150,7 +151,10 @@ defmodule Pleroma.Application do
|
|||
|
||||
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||
Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||
|
||||
# Note: disabled until prometheus-phx is integrated into prometheus-phoenix:
|
||||
# Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||
PrometheusPhx.setup()
|
||||
end
|
||||
|
||||
defp cachex_children do
|
||||
|
|
@ -164,7 +168,11 @@ defmodule Pleroma.Application do
|
|||
build_cachex("web_resp", limit: 2500),
|
||||
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
||||
build_cachex("failed_proxy_url", limit: 2500),
|
||||
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000)
|
||||
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
|
||||
build_cachex("chat_message_id_idempotency_key",
|
||||
expiration: chat_message_id_idempotency_key_expiration(),
|
||||
limit: 500_000
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -174,6 +182,9 @@ defmodule Pleroma.Application do
|
|||
defp idempotency_expiration,
|
||||
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
|
||||
|
||||
defp chat_message_id_idempotency_key_expiration,
|
||||
do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60))
|
||||
|
||||
defp seconds_valid_interval,
|
||||
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||
|
||||
|
|
@ -201,11 +212,14 @@ defmodule Pleroma.Application do
|
|||
]
|
||||
end
|
||||
|
||||
defp chat_child(_env, true) do
|
||||
[Pleroma.Web.ChatChannel.ChatChannelState]
|
||||
defp chat_child(true) do
|
||||
[
|
||||
Pleroma.Web.ChatChannel.ChatChannelState,
|
||||
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
||||
]
|
||||
end
|
||||
|
||||
defp chat_child(_, _), do: []
|
||||
defp chat_child(_), do: []
|
||||
|
||||
defp task_children(:test) do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
|
|||
def new do
|
||||
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
||||
|
||||
case Tesla.get(endpoint <> "/new") do
|
||||
case Pleroma.HTTP.get(endpoint <> "/new") do
|
||||
{:error, _} ->
|
||||
%{error: :kocaptcha_service_unavailable}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ defmodule Pleroma.Conversation do
|
|||
def maybe_create_recipientships(participation, activity) do
|
||||
participation = Repo.preload(participation, :recipients)
|
||||
|
||||
if participation.recipients |> Enum.empty?() do
|
||||
if Enum.empty?(participation.recipients) do
|
||||
recipients = User.get_all_by_ap_id(activity.recipients)
|
||||
RecipientShip.create(recipients, participation)
|
||||
end
|
||||
|
|
@ -69,10 +69,6 @@ defmodule Pleroma.Conversation do
|
|||
Enum.map(users, fn user ->
|
||||
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
|
||||
|
||||
unless invisible_conversation do
|
||||
User.increment_unread_conversation_count(conversation, user)
|
||||
end
|
||||
|
||||
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
|
||||
|
||||
{:ok, participation} =
|
||||
|
|
|
|||
|
|
@ -63,21 +63,10 @@ defmodule Pleroma.Conversation.Participation do
|
|||
end
|
||||
end
|
||||
|
||||
def mark_as_read(participation) do
|
||||
__MODULE__
|
||||
|> where(id: ^participation.id)
|
||||
|> update(set: [read: true])
|
||||
|> select([p], p)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [participation]} ->
|
||||
participation = Repo.preload(participation, :user)
|
||||
User.set_unread_conversation_count(participation.user)
|
||||
{:ok, participation}
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
def mark_as_read(%__MODULE__{} = participation) do
|
||||
participation
|
||||
|> change(read: true)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
|
||||
|
|
@ -93,7 +82,6 @@ defmodule Pleroma.Conversation.Participation do
|
|||
|> update([p], set: [read: true])
|
||||
|> Repo.update_all([])
|
||||
|
||||
{:ok, user} = User.set_unread_conversation_count(user)
|
||||
{:ok, user, []}
|
||||
end
|
||||
|
||||
|
|
@ -108,7 +96,6 @@ defmodule Pleroma.Conversation.Participation do
|
|||
|> select([p], p)
|
||||
|> Repo.update_all([])
|
||||
|
||||
{:ok, user} = User.set_unread_conversation_count(user)
|
||||
{:ok, user, participations}
|
||||
end
|
||||
|
||||
|
|
@ -220,6 +207,12 @@ defmodule Pleroma.Conversation.Participation do
|
|||
{:ok, Repo.preload(participation, :recipients, force: true)}
|
||||
end
|
||||
|
||||
@spec unread_count(User.t()) :: integer()
|
||||
def unread_count(%User{id: user_id}) do
|
||||
from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false)
|
||||
|> Repo.aggregate(:count, :id)
|
||||
end
|
||||
|
||||
def unread_conversation_count_for_user(user) do
|
||||
from(p in __MODULE__,
|
||||
where: p.user_id == ^user.id,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ defmodule Pleroma.Docs.JSON do
|
|||
|
||||
@spec compile :: :ok
|
||||
def compile do
|
||||
:persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions))
|
||||
descriptions =
|
||||
Pleroma.Web.ActivityPub.MRF.config_descriptions()
|
||||
|> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
|
||||
|
||||
:persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions))
|
||||
end
|
||||
|
||||
@spec compiled_descriptions :: Map.t()
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
Keyword.get(instance_config(), :notify_email, instance_config()[:email])
|
||||
end
|
||||
|
||||
defp user_url(user) do
|
||||
Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
|
||||
end
|
||||
|
||||
def test_email(mail_to \\ nil) do
|
||||
html_body = """
|
||||
<h3>Instance Test Email</h3>
|
||||
|
|
@ -69,8 +65,8 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
end
|
||||
|
||||
html_body = """
|
||||
<p>Reported by: <a href="#{user_url(reporter)}">#{reporter.nickname}</a></p>
|
||||
<p>Reported Account: <a href="#{user_url(account)}">#{account.nickname}</a></p>
|
||||
<p>Reported by: <a href="#{reporter.ap_id}">#{reporter.nickname}</a></p>
|
||||
<p>Reported Account: <a href="#{account.ap_id}">#{account.nickname}</a></p>
|
||||
#{comment_html}
|
||||
#{statuses_html}
|
||||
<p>
|
||||
|
|
@ -86,7 +82,7 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
|
||||
def new_unapproved_registration(to, account) do
|
||||
html_body = """
|
||||
<p>New account for review: <a href="#{user_url(account)}">@#{account.nickname}</a></p>
|
||||
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
|
||||
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
|
||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -202,4 +202,30 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
|
||||
Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
|
||||
end
|
||||
|
||||
def backup_is_ready_email(backup, admin_user_id \\ nil) do
|
||||
%{user: user} = Pleroma.Repo.preload(backup, :user)
|
||||
download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
|
||||
|
||||
html_body =
|
||||
if is_nil(admin_user_id) do
|
||||
"""
|
||||
<p>You requested a full backup of your Pleroma account. It's ready for download:</p>
|
||||
<p><a href="#{download_url}">#{download_url}</a></p>
|
||||
"""
|
||||
else
|
||||
admin = Pleroma.Repo.get(User, admin_user_id)
|
||||
|
||||
"""
|
||||
<p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
|
||||
<p><a href="#{download_url}">#{download_url}</a></p>
|
||||
"""
|
||||
end
|
||||
|
||||
new()
|
||||
|> to(recipient(user))
|
||||
|> from(sender())
|
||||
|> subject("Your account archive is ready")
|
||||
|> html_body(html_body)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
|
||||
defp download_archive(url, sha) do
|
||||
with {:ok, %{body: archive}} <- Tesla.get(url) do
|
||||
with {:ok, %{body: archive}} <- Pleroma.HTTP.get(url) do
|
||||
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
|
||||
{:ok, archive}
|
||||
else
|
||||
|
|
@ -617,7 +617,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
|
||||
defp update_sha_and_save_metadata(pack, data) do
|
||||
with {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]),
|
||||
with {:ok, %{body: zip}} <- Pleroma.HTTP.get(data[:"fallback-src"]),
|
||||
:ok <- validate_has_all_files(pack, zip) do
|
||||
fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16()
|
||||
|
||||
|
|
|
|||
19
lib/pleroma/helpers/inet_helper.ex
Normal file
19
lib/pleroma/helpers/inet_helper.ex
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Helpers.InetHelper do
|
||||
def parse_address(ip) when is_tuple(ip) do
|
||||
{:ok, ip}
|
||||
end
|
||||
|
||||
def parse_address(ip) when is_binary(ip) do
|
||||
ip
|
||||
|> String.to_charlist()
|
||||
|> parse_address()
|
||||
end
|
||||
|
||||
def parse_address(ip) do
|
||||
:inet.parse_address(ip)
|
||||
end
|
||||
end
|
||||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Instances do
|
|||
defdelegate reachable?(url_or_host), to: @adapter
|
||||
defdelegate set_reachable(url_or_host), to: @adapter
|
||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||
defdelegate get_consistently_unreachable(), to: @adapter
|
||||
|
||||
def set_consistently_unreachable(url_or_host),
|
||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||
|
|
|
|||
|
|
@ -119,6 +119,17 @@ defmodule Pleroma.Instances.Instance do
|
|||
|
||||
def set_unreachable(_, _), do: {:error, nil}
|
||||
|
||||
def get_consistently_unreachable do
|
||||
reachability_datetime_threshold = Instances.reachability_datetime_threshold()
|
||||
|
||||
from(i in Instance,
|
||||
where: ^reachability_datetime_threshold > i.unreachable_since,
|
||||
order_by: i.unreachable_since,
|
||||
select: {i.host, i.unreachable_since}
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
defp parse_datetime(datetime) when is_binary(datetime) do
|
||||
NaiveDateTime.from_iso8601(datetime)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.MIME do
|
||||
@moduledoc """
|
||||
Returns the mime-type of a binary and optionally a normalized file-name.
|
||||
"""
|
||||
@default "application/octet-stream"
|
||||
@read_bytes 35
|
||||
|
||||
@spec file_mime_type(String.t(), String.t()) ::
|
||||
{:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error
|
||||
def file_mime_type(path, filename) do
|
||||
with {:ok, content_type} <- file_mime_type(path),
|
||||
filename <- fix_extension(filename, content_type) do
|
||||
{:ok, content_type, filename}
|
||||
end
|
||||
end
|
||||
|
||||
@spec file_mime_type(String.t()) :: {:ok, String.t()} | {:error, any()} | :error
|
||||
def file_mime_type(filename) do
|
||||
File.open(filename, [:read], fn f ->
|
||||
check_mime_type(IO.binread(f, @read_bytes))
|
||||
end)
|
||||
end
|
||||
|
||||
def bin_mime_type(binary, filename) do
|
||||
with {:ok, content_type} <- bin_mime_type(binary),
|
||||
filename <- fix_extension(filename, content_type) do
|
||||
{:ok, content_type, filename}
|
||||
end
|
||||
end
|
||||
|
||||
@spec bin_mime_type(binary()) :: {:ok, String.t()} | :error
|
||||
def bin_mime_type(<<head::binary-size(@read_bytes), _::binary>>) do
|
||||
{:ok, check_mime_type(head)}
|
||||
end
|
||||
|
||||
def bin_mime_type(_), do: :error
|
||||
|
||||
def mime_type(<<_::binary>>), do: {:ok, @default}
|
||||
|
||||
defp fix_extension(filename, content_type) do
|
||||
parts = String.split(filename, ".")
|
||||
|
||||
new_filename =
|
||||
if length(parts) > 1 do
|
||||
Enum.drop(parts, -1) |> Enum.join(".")
|
||||
else
|
||||
Enum.join(parts)
|
||||
end
|
||||
|
||||
cond do
|
||||
content_type == "application/octet-stream" ->
|
||||
filename
|
||||
|
||||
ext = List.first(MIME.extensions(content_type)) ->
|
||||
new_filename <> "." <> ext
|
||||
|
||||
true ->
|
||||
Enum.join([new_filename, String.split(content_type, "/") |> List.last()], ".")
|
||||
end
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, _::binary>>) do
|
||||
"image/png"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x47, 0x49, 0x46, 0x38, _, 0x61, _::binary>>) do
|
||||
"image/gif"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0xFF, 0xD8, 0xFF, _::binary>>) do
|
||||
"image/jpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x1A, 0x45, 0xDF, 0xA3, _::binary>>) do
|
||||
"video/webm"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70, _::binary>>) do
|
||||
"video/mp4"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x49, 0x44, 0x33, _::binary>>) do
|
||||
"audio/mpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<255, 251, _, 68, 0, 0, 0, 0, _::binary>>) do
|
||||
"audio/mpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(
|
||||
<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::size(160), 0x80, 0x74, 0x68, 0x65,
|
||||
0x6F, 0x72, 0x61, _::binary>>
|
||||
) do
|
||||
"video/ogg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::binary>>) do
|
||||
"audio/ogg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<"RIFF", _::binary-size(4), "WAVE", _::binary>>) do
|
||||
"audio/wav"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<"RIFF", _::binary-size(4), "WEBP", _::binary>>) do
|
||||
"image/webp"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<"RIFF", _::binary-size(4), "AVI.", _::binary>>) do
|
||||
"video/avi"
|
||||
end
|
||||
|
||||
defp check_mime_type(_) do
|
||||
@default
|
||||
end
|
||||
end
|
||||
|
|
@ -655,6 +655,16 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} deleted chat message ##{subject_id}"
|
||||
end
|
||||
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "create_backup",
|
||||
"subject" => %{"nickname" => user_nickname}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} requested account backup for @#{user_nickname}"
|
||||
end
|
||||
|
||||
defp nicknames_to_string(nicknames) do
|
||||
nicknames
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ defmodule Pleroma.Notification do
|
|||
move
|
||||
pleroma:chat_mention
|
||||
pleroma:emoji_reaction
|
||||
pleroma:report
|
||||
reblog
|
||||
}
|
||||
|
||||
|
|
@ -367,7 +368,7 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
|
||||
def create_notifications(%Activity{data: %{"type" => type}} = activity, options)
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact"] do
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do
|
||||
do_create_notifications(activity, options)
|
||||
end
|
||||
|
||||
|
|
@ -410,6 +411,9 @@ defmodule Pleroma.Notification do
|
|||
"EmojiReact" ->
|
||||
"pleroma:emoji_reaction"
|
||||
|
||||
"Flag" ->
|
||||
"pleroma:report"
|
||||
|
||||
# Compatibility with old reactions
|
||||
"EmojiReaction" ->
|
||||
"pleroma:emoji_reaction"
|
||||
|
|
@ -467,7 +471,7 @@ defmodule Pleroma.Notification do
|
|||
def get_notified_from_activity(activity, local_only \\ true)
|
||||
|
||||
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
|
||||
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do
|
||||
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do
|
||||
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
|
||||
|
||||
potential_receivers =
|
||||
|
|
@ -503,6 +507,10 @@ defmodule Pleroma.Notification do
|
|||
[object_id]
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag"}}) do
|
||||
User.all_superusers() |> Enum.map(fn user -> user.ap_id end)
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(activity) do
|
||||
[]
|
||||
|> Utils.maybe_notify_to_recipients(activity)
|
||||
|
|
|
|||
|
|
@ -232,8 +232,24 @@ defmodule Pleroma.Object.Fetcher do
|
|||
|> sign_fetch(id, date)
|
||||
|
||||
case HTTP.get(id, headers) do
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 ->
|
||||
{:ok, body}
|
||||
{:ok, %{body: body, status: code, headers: headers}} when code in 200..299 ->
|
||||
case List.keyfind(headers, "content-type", 0) do
|
||||
{_, content_type} ->
|
||||
case Plug.Conn.Utils.media_type(content_type) do
|
||||
{:ok, "application", "activity+json", _} ->
|
||||
{:ok, body}
|
||||
|
||||
{:ok, "application", "ld+json",
|
||||
%{"profile" => "https://www.w3.org/ns/activitystreams"}} ->
|
||||
{:ok, body}
|
||||
|
||||
_ ->
|
||||
{:error, {:content_type, content_type}}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, {:content_type, nil}}
|
||||
end
|
||||
|
||||
{:ok, %{status: code}} when code in [404, 410] ->
|
||||
{:error, "Object has been deleted"}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ defmodule Pleroma.Stats do
|
|||
|
||||
@impl true
|
||||
def init(_args) do
|
||||
if Pleroma.Config.get(:env) == :test, do: :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
||||
{:ok, nil, {:continue, :calculate_stats}}
|
||||
end
|
||||
|
||||
|
|
@ -32,11 +31,6 @@ defmodule Pleroma.Stats do
|
|||
GenServer.call(__MODULE__, :force_update)
|
||||
end
|
||||
|
||||
@doc "Performs collect stats"
|
||||
def do_collect do
|
||||
GenServer.cast(__MODULE__, :run_update)
|
||||
end
|
||||
|
||||
@doc "Returns stats data"
|
||||
@spec get_stats() :: %{
|
||||
domain_count: non_neg_integer(),
|
||||
|
|
@ -111,7 +105,11 @@ defmodule Pleroma.Stats do
|
|||
@impl true
|
||||
def handle_continue(:calculate_stats, _) do
|
||||
stats = calculate_stat_data()
|
||||
Process.send_after(self(), :run_update, @interval)
|
||||
|
||||
unless Pleroma.Config.get(:env) == :test do
|
||||
Process.send_after(self(), :run_update, @interval)
|
||||
end
|
||||
|
||||
{:noreply, stats}
|
||||
end
|
||||
|
||||
|
|
@ -126,13 +124,6 @@ defmodule Pleroma.Stats do
|
|||
{:reply, state, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast(:run_update, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:run_update, _) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
|
||||
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
|
||||
@doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct."
|
||||
def store(upload, opts \\ []) do
|
||||
opts = get_opts(opts)
|
||||
|
||||
|
|
@ -139,14 +140,13 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
|
||||
defp prepare_upload(%Plug.Upload{} = file, opts) do
|
||||
with :ok <- check_file_size(file.path, opts.size_limit),
|
||||
{:ok, content_type, name} <- Pleroma.MIME.file_mime_type(file.path, file.filename) do
|
||||
with :ok <- check_file_size(file.path, opts.size_limit) do
|
||||
{:ok,
|
||||
%__MODULE__{
|
||||
id: UUID.generate(),
|
||||
name: name,
|
||||
name: file.filename,
|
||||
tempfile: file.path,
|
||||
content_type: content_type
|
||||
content_type: file.content_type
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
|
@ -154,16 +154,17 @@ defmodule Pleroma.Upload do
|
|||
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
|
||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
||||
hash = String.downcase(Base.encode16(:crypto.hash(:sha256, data)))
|
||||
hash = Base.encode16(:crypto.hash(:sha256, data), lower: true)
|
||||
|
||||
with :ok <- check_binary_size(data, opts.size_limit),
|
||||
tmp_path <- tempfile_for_image(data),
|
||||
{:ok, content_type, name} <-
|
||||
Pleroma.MIME.bin_mime_type(data, hash <> "." <> parsed["filetype"]) do
|
||||
{:ok, %{mime_type: content_type}} <-
|
||||
Majic.perform({:bytes, data}, pool: Pleroma.MajicPool),
|
||||
[ext | _] <- MIME.extensions(content_type) do
|
||||
{:ok,
|
||||
%__MODULE__{
|
||||
id: UUID.generate(),
|
||||
name: name,
|
||||
name: hash <> "." <> ext,
|
||||
tempfile: tmp_path,
|
||||
content_type: content_type
|
||||
}}
|
||||
|
|
@ -172,7 +173,7 @@ defmodule Pleroma.Upload do
|
|||
|
||||
# For Mix.Tasks.MigrateLocalUploads
|
||||
defp prepare_upload(%__MODULE__{tempfile: path} = upload, _opts) do
|
||||
with {:ok, content_type} <- Pleroma.MIME.file_mime_type(path) do
|
||||
with {:ok, %{mime_type: content_type}} <- Majic.perform(path, pool: Pleroma.MajicPool) do
|
||||
{:ok, %__MODULE__{upload | content_type: content_type}}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ defmodule Pleroma.User do
|
|||
field(:note_count, :integer, default: 0)
|
||||
field(:follower_count, :integer, default: 0)
|
||||
field(:following_count, :integer, default: 0)
|
||||
field(:locked, :boolean, default: false)
|
||||
field(:is_locked, :boolean, default: false)
|
||||
field(:confirmation_pending, :boolean, default: false)
|
||||
field(:password_reset_pending, :boolean, default: false)
|
||||
field(:approval_pending, :boolean, default: false)
|
||||
|
|
@ -128,7 +128,6 @@ defmodule Pleroma.User do
|
|||
field(:hide_followers, :boolean, default: false)
|
||||
field(:hide_follows, :boolean, default: false)
|
||||
field(:hide_favorites, :boolean, default: true)
|
||||
field(:unread_conversation_count, :integer, default: 0)
|
||||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:email_notifications, :map, default: %{"digest" => false})
|
||||
field(:mascot, :map, default: nil)
|
||||
|
|
@ -136,7 +135,7 @@ defmodule Pleroma.User do
|
|||
field(:pleroma_settings_store, :map, default: %{})
|
||||
field(:fields, {:array, :map}, default: [])
|
||||
field(:raw_fields, {:array, :map}, default: [])
|
||||
field(:discoverable, :boolean, default: false)
|
||||
field(:is_discoverable, :boolean, default: false)
|
||||
field(:invisible, :boolean, default: false)
|
||||
field(:allow_following_move, :boolean, default: true)
|
||||
field(:skip_thread_containment, :boolean, default: false)
|
||||
|
|
@ -426,7 +425,6 @@ defmodule Pleroma.User do
|
|||
params,
|
||||
[
|
||||
:bio,
|
||||
:name,
|
||||
:emoji,
|
||||
:ap_id,
|
||||
:inbox,
|
||||
|
|
@ -436,7 +434,7 @@ defmodule Pleroma.User do
|
|||
:avatar,
|
||||
:ap_enabled,
|
||||
:banner,
|
||||
:locked,
|
||||
:is_locked,
|
||||
:last_refreshed_at,
|
||||
:uri,
|
||||
:follower_address,
|
||||
|
|
@ -448,14 +446,16 @@ defmodule Pleroma.User do
|
|||
:follower_count,
|
||||
:fields,
|
||||
:following_count,
|
||||
:discoverable,
|
||||
:is_discoverable,
|
||||
:invisible,
|
||||
:actor_type,
|
||||
:also_known_as,
|
||||
:accepts_chat_messages
|
||||
]
|
||||
)
|
||||
|> validate_required([:name, :ap_id])
|
||||
|> cast(params, [:name], empty_values: [])
|
||||
|> validate_required([:ap_id])
|
||||
|> validate_required([:name], trim: false)
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, @email_regex)
|
||||
|> validate_length(:bio, max: bio_limit)
|
||||
|
|
@ -479,7 +479,7 @@ defmodule Pleroma.User do
|
|||
:public_key,
|
||||
:inbox,
|
||||
:shared_inbox,
|
||||
:locked,
|
||||
:is_locked,
|
||||
:no_rich_text,
|
||||
:default_scope,
|
||||
:banner,
|
||||
|
|
@ -495,7 +495,7 @@ defmodule Pleroma.User do
|
|||
:fields,
|
||||
:raw_fields,
|
||||
:pleroma_settings_store,
|
||||
:discoverable,
|
||||
:is_discoverable,
|
||||
:actor_type,
|
||||
:also_known_as,
|
||||
:accepts_chat_messages
|
||||
|
|
@ -765,6 +765,16 @@ defmodule Pleroma.User do
|
|||
follow_all(user, autofollowed_users)
|
||||
end
|
||||
|
||||
defp autofollowing_users(user) do
|
||||
candidates = Config.get([:instance, :autofollowing_nicknames])
|
||||
|
||||
User.Query.build(%{nickname: candidates, local: true, deactivated: false})
|
||||
|> Repo.all()
|
||||
|> Enum.each(&follow(&1, user, :follow_accept))
|
||||
|
||||
{:ok, :success}
|
||||
end
|
||||
|
||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||
def register(%Ecto.Changeset{} = changeset) do
|
||||
with {:ok, user} <- Repo.insert(changeset) do
|
||||
|
|
@ -787,6 +797,7 @@ defmodule Pleroma.User do
|
|||
|
||||
def post_register_action(%User{approval_pending: false, confirmation_pending: false} = user) do
|
||||
with {:ok, user} <- autofollow_users(user),
|
||||
{:ok, _} <- autofollowing_users(user),
|
||||
{:ok, user} <- set_cache(user),
|
||||
{:ok, _} <- send_welcome_email(user),
|
||||
{:ok, _} <- send_welcome_message(user),
|
||||
|
|
@ -879,7 +890,7 @@ defmodule Pleroma.User do
|
|||
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
|
||||
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
|
||||
follow(follower, followed, :follow_pending)
|
||||
end
|
||||
|
||||
|
|
@ -986,7 +997,7 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def locked?(%User{} = user) do
|
||||
user.locked || false
|
||||
user.is_locked || false
|
||||
end
|
||||
|
||||
def get_by_id(id) do
|
||||
|
|
@ -1325,47 +1336,6 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def set_unread_conversation_count(%User{local: true} = user) do
|
||||
unread_query = Participation.unread_conversation_count_for_user(user)
|
||||
|
||||
User
|
||||
|> join(:inner, [u], p in subquery(unread_query))
|
||||
|> update([u, p],
|
||||
set: [unread_conversation_count: p.count]
|
||||
)
|
||||
|> where([u], u.id == ^user.id)
|
||||
|> select([u], u)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def set_unread_conversation_count(user), do: {:ok, user}
|
||||
|
||||
def increment_unread_conversation_count(conversation, %User{local: true} = user) do
|
||||
unread_query =
|
||||
Participation.unread_conversation_count_for_user(user)
|
||||
|> where([p], p.conversation_id == ^conversation.id)
|
||||
|
||||
User
|
||||
|> join(:inner, [u], p in subquery(unread_query))
|
||||
|> update([u, p],
|
||||
inc: [unread_conversation_count: 1]
|
||||
)
|
||||
|> where([u], u.id == ^user.id)
|
||||
|> where([u, p], p.count == 0)
|
||||
|> select([u], u)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def increment_unread_conversation_count(_, user), do: {:ok, user}
|
||||
|
||||
@spec get_users_from_set([String.t()], keyword()) :: [User.t()]
|
||||
def get_users_from_set(ap_ids, opts \\ []) do
|
||||
local_only = Keyword.get(opts, :local_only, true)
|
||||
|
|
@ -1386,14 +1356,48 @@ defmodule Pleroma.User do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec mute(User.t(), User.t(), boolean()) ::
|
||||
@spec mute(User.t(), User.t(), map()) ::
|
||||
{:ok, list(UserRelationship.t())} | {:error, String.t()}
|
||||
def mute(%User{} = muter, %User{} = mutee, notifications? \\ true) do
|
||||
add_to_mutes(muter, mutee, notifications?)
|
||||
def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
|
||||
notifications? = Map.get(params, :notifications, true)
|
||||
expires_in = Map.get(params, :expires_in, 0)
|
||||
|
||||
with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee),
|
||||
{:ok, user_notification_mute} <-
|
||||
(notifications? && UserRelationship.create_notification_mute(muter, mutee)) ||
|
||||
{:ok, nil} do
|
||||
if expires_in > 0 do
|
||||
Pleroma.Workers.MuteExpireWorker.enqueue(
|
||||
"unmute_user",
|
||||
%{"muter_id" => muter.id, "mutee_id" => mutee.id},
|
||||
schedule_in: expires_in
|
||||
)
|
||||
end
|
||||
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
end
|
||||
|
||||
def unmute(%User{} = muter, %User{} = mutee) do
|
||||
remove_from_mutes(muter, mutee)
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(muter, mutee) do
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
|
||||
def unmute(muter_id, mutee_id) do
|
||||
with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)},
|
||||
{:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do
|
||||
unmute(muter, mutee)
|
||||
else
|
||||
{who, result} = error ->
|
||||
Logger.warn(
|
||||
"User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def subscribe(%User{} = subscriber, %User{} = target) do
|
||||
|
|
@ -1656,7 +1660,7 @@ defmodule Pleroma.User do
|
|||
note_count: 0,
|
||||
follower_count: 0,
|
||||
following_count: 0,
|
||||
locked: false,
|
||||
is_locked: false,
|
||||
confirmation_pending: false,
|
||||
password_reset_pending: false,
|
||||
approval_pending: false,
|
||||
|
|
@ -1673,7 +1677,7 @@ defmodule Pleroma.User do
|
|||
pleroma_settings_store: %{},
|
||||
fields: [],
|
||||
raw_fields: [],
|
||||
discoverable: false,
|
||||
is_discoverable: false,
|
||||
also_known_as: []
|
||||
})
|
||||
end
|
||||
|
|
@ -2393,23 +2397,6 @@ defmodule Pleroma.User do
|
|||
UserRelationship.delete_block(user, blocked)
|
||||
end
|
||||
|
||||
defp add_to_mutes(%User{} = user, %User{} = muted_user, notifications?) do
|
||||
with {:ok, user_mute} <- UserRelationship.create_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
(notifications? && UserRelationship.create_notification_mute(user, muted_user)) ||
|
||||
{:ok, nil} do
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
end
|
||||
|
||||
defp remove_from_mutes(user, %User{} = muted_user) do
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(user, muted_user) do
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
|
||||
def set_invisible(user, invisible) do
|
||||
params = %{invisible: invisible}
|
||||
|
||||
|
|
|
|||
258
lib/pleroma/user/backup.ex
Normal file
258
lib/pleroma/user/backup.ex
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.Backup do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Workers.BackupWorker
|
||||
|
||||
schema "backups" do
|
||||
field(:content_type, :string)
|
||||
field(:file_name, :string)
|
||||
field(:file_size, :integer, default: 0)
|
||||
field(:processed, :boolean, default: false)
|
||||
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def create(user, admin_id \\ nil) do
|
||||
with :ok <- validate_email_enabled(),
|
||||
:ok <- validate_user_email(user),
|
||||
:ok <- validate_limit(user, admin_id),
|
||||
{:ok, backup} <- user |> new() |> Repo.insert() do
|
||||
BackupWorker.process(backup, admin_id)
|
||||
end
|
||||
end
|
||||
|
||||
def new(user) do
|
||||
rand_str = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||
datetime = Calendar.NaiveDateTime.Format.iso8601_basic(NaiveDateTime.utc_now())
|
||||
name = "archive-#{user.nickname}-#{datetime}-#{rand_str}.zip"
|
||||
|
||||
%__MODULE__{
|
||||
user_id: user.id,
|
||||
content_type: "application/zip",
|
||||
file_name: name
|
||||
}
|
||||
end
|
||||
|
||||
def delete(backup) do
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
|
||||
with :ok <- uploader.delete_file(Path.join("backups", backup.file_name)) do
|
||||
Repo.delete(backup)
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_limit(_user, admin_id) when is_binary(admin_id), do: :ok
|
||||
|
||||
defp validate_limit(user, nil) do
|
||||
case get_last(user.id) do
|
||||
%__MODULE__{inserted_at: inserted_at} ->
|
||||
days = Pleroma.Config.get([__MODULE__, :limit_days])
|
||||
diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days)
|
||||
|
||||
if diff > days do
|
||||
:ok
|
||||
else
|
||||
{:error,
|
||||
dngettext(
|
||||
"errors",
|
||||
"Last export was less than a day ago",
|
||||
"Last export was less than %{days} days ago",
|
||||
days,
|
||||
days: days
|
||||
)}
|
||||
end
|
||||
|
||||
nil ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_email_enabled do
|
||||
if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
|
||||
:ok
|
||||
else
|
||||
{:error, dgettext("errors", "Backups require enabled email")}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_user_email(%User{email: nil}) do
|
||||
{:error, dgettext("errors", "Email is required")}
|
||||
end
|
||||
|
||||
defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok
|
||||
|
||||
def get_last(user_id) do
|
||||
__MODULE__
|
||||
|> where(user_id: ^user_id)
|
||||
|> order_by(desc: :id)
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def list(%User{id: user_id}) do
|
||||
__MODULE__
|
||||
|> where(user_id: ^user_id)
|
||||
|> order_by(desc: :id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def remove_outdated(%__MODULE__{id: latest_id, user_id: user_id}) do
|
||||
__MODULE__
|
||||
|> where(user_id: ^user_id)
|
||||
|> where([b], b.id != ^latest_id)
|
||||
|> Repo.all()
|
||||
|> Enum.each(&BackupWorker.delete/1)
|
||||
end
|
||||
|
||||
def get(id), do: Repo.get(__MODULE__, id)
|
||||
|
||||
def process(%__MODULE__{} = backup) do
|
||||
with {:ok, zip_file} <- export(backup),
|
||||
{:ok, %{size: size}} <- File.stat(zip_file),
|
||||
{:ok, _upload} <- upload(backup, zip_file) do
|
||||
backup
|
||||
|> cast(%{file_size: size, processed: true}, [:file_size, :processed])
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
||||
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
|
||||
def export(%__MODULE__{} = backup) do
|
||||
backup = Repo.preload(backup, :user)
|
||||
name = String.trim_trailing(backup.file_name, ".zip")
|
||||
dir = dir(name)
|
||||
|
||||
with :ok <- File.mkdir(dir),
|
||||
:ok <- actor(dir, backup.user),
|
||||
:ok <- statuses(dir, backup.user),
|
||||
:ok <- likes(dir, backup.user),
|
||||
:ok <- bookmarks(dir, backup.user),
|
||||
{:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir),
|
||||
{:ok, _} <- File.rm_rf(dir) do
|
||||
{:ok, to_string(zip_path)}
|
||||
end
|
||||
end
|
||||
|
||||
def dir(name) do
|
||||
dir = Pleroma.Config.get([__MODULE__, :dir]) || System.tmp_dir!()
|
||||
Path.join(dir, name)
|
||||
end
|
||||
|
||||
def upload(%__MODULE__{} = backup, zip_path) do
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
|
||||
upload = %Pleroma.Upload{
|
||||
name: backup.file_name,
|
||||
tempfile: zip_path,
|
||||
content_type: backup.content_type,
|
||||
path: Path.join("backups", backup.file_name)
|
||||
}
|
||||
|
||||
with {:ok, _} <- Pleroma.Uploaders.Uploader.put_file(uploader, upload),
|
||||
:ok <- File.rm(zip_path) do
|
||||
{:ok, upload}
|
||||
end
|
||||
end
|
||||
|
||||
defp actor(dir, user) do
|
||||
with {:ok, json} <-
|
||||
UserView.render("user.json", %{user: user})
|
||||
|> Map.merge(%{"likes" => "likes.json", "bookmarks" => "bookmarks.json"})
|
||||
|> Jason.encode() do
|
||||
File.write(Path.join(dir, "actor.json"), json)
|
||||
end
|
||||
end
|
||||
|
||||
defp write_header(file, name) do
|
||||
IO.write(
|
||||
file,
|
||||
"""
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "#{name}.json",
|
||||
"type": "OrderedCollection",
|
||||
"orderedItems": [
|
||||
|
||||
"""
|
||||
)
|
||||
end
|
||||
|
||||
defp write(query, dir, name, fun) do
|
||||
path = Path.join(dir, "#{name}.json")
|
||||
|
||||
with {:ok, file} <- File.open(path, [:write, :utf8]),
|
||||
:ok <- write_header(file, name) do
|
||||
total =
|
||||
query
|
||||
|> Pleroma.Repo.chunk_stream(100)
|
||||
|> Enum.reduce(0, fn i, acc ->
|
||||
with {:ok, data} <- fun.(i),
|
||||
{:ok, str} <- Jason.encode(data),
|
||||
:ok <- IO.write(file, str <> ",\n") do
|
||||
acc + 1
|
||||
else
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
with :ok <- :file.pwrite(file, {:eof, -2}, "\n],\n \"totalItems\": #{total}}") do
|
||||
File.close(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp bookmarks(dir, %{id: user_id} = _user) do
|
||||
Bookmark
|
||||
|> where(user_id: ^user_id)
|
||||
|> join(:inner, [b], activity in assoc(b, :activity))
|
||||
|> select([b, a], %{id: b.id, object: fragment("(?)->>'object'", a.data)})
|
||||
|> write(dir, "bookmarks", fn a -> {:ok, a.object} end)
|
||||
end
|
||||
|
||||
defp likes(dir, user) do
|
||||
user.ap_id
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_type("Like")
|
||||
|> select([like], %{id: like.id, object: fragment("(?)->>'object'", like.data)})
|
||||
|> write(dir, "likes", fn a -> {:ok, a.object} end)
|
||||
end
|
||||
|
||||
defp statuses(dir, user) do
|
||||
opts =
|
||||
%{}
|
||||
|> Map.put(:type, ["Create", "Announce"])
|
||||
|> Map.put(:actor_id, user.ap_id)
|
||||
|
||||
[
|
||||
[Pleroma.Constants.as_public(), user.ap_id],
|
||||
User.following(user),
|
||||
Pleroma.List.memberships(user)
|
||||
]
|
||||
|> Enum.concat()
|
||||
|> ActivityPub.fetch_activities_query(opts)
|
||||
|> write(dir, "outbox", fn a ->
|
||||
with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do
|
||||
{:ok, Map.delete(activity, "@context")}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -43,6 +43,7 @@ defmodule Pleroma.User.Query do
|
|||
active: boolean(),
|
||||
deactivated: boolean(),
|
||||
need_approval: boolean(),
|
||||
unconfirmed: boolean(),
|
||||
is_admin: boolean(),
|
||||
is_moderator: boolean(),
|
||||
super_users: boolean(),
|
||||
|
|
@ -55,7 +56,8 @@ defmodule Pleroma.User.Query do
|
|||
ap_id: [String.t()],
|
||||
order_by: term(),
|
||||
select: term(),
|
||||
limit: pos_integer()
|
||||
limit: pos_integer(),
|
||||
actor_types: [String.t()]
|
||||
}
|
||||
| map()
|
||||
|
||||
|
|
@ -114,6 +116,10 @@ defmodule Pleroma.User.Query do
|
|||
where(query, [u], u.is_admin == ^bool)
|
||||
end
|
||||
|
||||
defp compose_query({:actor_types, actor_types}, query) when is_list(actor_types) do
|
||||
where(query, [u], u.actor_type in ^actor_types)
|
||||
end
|
||||
|
||||
defp compose_query({:is_moderator, bool}, query) do
|
||||
where(query, [u], u.is_moderator == ^bool)
|
||||
end
|
||||
|
|
@ -156,6 +162,10 @@ defmodule Pleroma.User.Query do
|
|||
where(query, [u], u.approval_pending)
|
||||
end
|
||||
|
||||
defp compose_query({:unconfirmed, _}, query) do
|
||||
where(query, [u], u.confirmation_pending)
|
||||
end
|
||||
|
||||
defp compose_query({:followers, %User{id: id}}, query) do
|
||||
query
|
||||
|> where([u], u.id != ^id)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ defmodule Pleroma.User.Search do
|
|||
end
|
||||
|
||||
defp filter_discoverable_users(query) do
|
||||
from(q in query, where: q.discoverable == true)
|
||||
from(q in query, where: q.is_discoverable == true)
|
||||
end
|
||||
|
||||
defp filter_internal_users(query) do
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ defmodule Pleroma.Web do
|
|||
def channel do
|
||||
quote do
|
||||
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
||||
use Phoenix.Channel
|
||||
import Phoenix.Channel
|
||||
import Pleroma.Web.Gettext
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -827,7 +827,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
query =
|
||||
from([activity] in query,
|
||||
where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
|
||||
where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
|
||||
where:
|
||||
fragment(
|
||||
"not (?->'to' \\?| ?) or ? = ?",
|
||||
activity.data,
|
||||
^mutes,
|
||||
activity.actor,
|
||||
^user.ap_id
|
||||
)
|
||||
)
|
||||
|
||||
unless opts[:skip_preload] do
|
||||
|
|
@ -930,16 +937,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_muted_reblogs(query, _), do: query
|
||||
|
||||
defp restrict_instance(query, %{instance: instance}) do
|
||||
users =
|
||||
from(
|
||||
u in User,
|
||||
select: u.ap_id,
|
||||
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
||||
)
|
||||
|> Repo.all()
|
||||
|
||||
from(activity in query, where: activity.actor in ^users)
|
||||
defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_instance(query, _), do: query
|
||||
|
|
@ -1228,11 +1230,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{String.trim(name, ":"), url}
|
||||
end)
|
||||
|
||||
locked = data["manuallyApprovesFollowers"] || false
|
||||
is_locked = data["manuallyApprovesFollowers"] || false
|
||||
capabilities = data["capabilities"] || %{}
|
||||
accepts_chat_messages = capabilities["acceptsChatMessages"]
|
||||
data = Transmogrifier.maybe_fix_user_object(data)
|
||||
discoverable = data["discoverable"] || false
|
||||
is_discoverable = data["discoverable"] || false
|
||||
invisible = data["invisible"] || false
|
||||
actor_type = data["type"] || "Person"
|
||||
|
||||
|
|
@ -1257,8 +1259,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
banner: banner,
|
||||
fields: fields,
|
||||
emoji: emojis,
|
||||
locked: locked,
|
||||
discoverable: discoverable,
|
||||
is_locked: is_locked,
|
||||
is_discoverable: is_discoverable,
|
||||
invisible: invisible,
|
||||
avatar: avatar,
|
||||
name: data["name"],
|
||||
|
|
@ -1371,6 +1373,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{:ok, data} <- user_data_from_user_object(data) do
|
||||
{:ok, maybe_update_follow_information(data)}
|
||||
else
|
||||
# If this has been deleted, only log a debug and not an error
|
||||
{:error, "Object has been deleted" = e} ->
|
||||
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||
{:error, e}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
when action in [:read_inbox, :update_outbox, :whoami, :upload_media]
|
||||
)
|
||||
|
||||
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:upload_media])
|
||||
|
||||
plug(
|
||||
Pleroma.Web.Plugs.Cache,
|
||||
[query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2]
|
||||
|
|
@ -412,7 +414,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
object =
|
||||
object
|
||||
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||
|> Map.put("attributedTo", user.ap_id())
|
||||
|> Map.put("attributedTo", user.ap_id)
|
||||
|> Transmogrifier.fix_object()
|
||||
|
||||
ActivityPub.create(%{
|
||||
|
|
@ -456,7 +458,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
actor = user.ap_id()
|
||||
actor = user.ap_id
|
||||
|
||||
params =
|
||||
params
|
||||
|
|
@ -523,19 +525,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{new_user, for_user}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Endpoint based on <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>
|
||||
|
||||
Parameters:
|
||||
- (required) `file`: data of the media
|
||||
- (optionnal) `description`: description of the media, intended for accessibility
|
||||
|
||||
Response:
|
||||
- HTTP Code: 201 Created
|
||||
- HTTP Body: ActivityPub object to be inserted into another's `attachment` field
|
||||
|
||||
Note: Will not point to a URL with a `Location` header because no standalone Activity has been created.
|
||||
"""
|
||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,62 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
require Logger
|
||||
|
||||
@mrf_config_descriptions [
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf,
|
||||
tab: :mrf,
|
||||
label: "MRF",
|
||||
type: :group,
|
||||
description: "General MRF settings",
|
||||
children: [
|
||||
%{
|
||||
key: :policies,
|
||||
type: [:module, {:list, :module}],
|
||||
description:
|
||||
"A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
|
||||
suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
|
||||
},
|
||||
%{
|
||||
key: :transparency,
|
||||
label: "MRF transparency",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
|
||||
},
|
||||
%{
|
||||
key: :transparency_exclusions,
|
||||
label: "MRF transparency exclusions",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
|
||||
suggestions: [
|
||||
"exclusion.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@default_description %{
|
||||
label: "",
|
||||
description: ""
|
||||
}
|
||||
|
||||
@required_description_keys [:key, :related_policy]
|
||||
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|
|
@ -51,8 +106,6 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
|
||||
end
|
||||
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
|
||||
def describe(policies) do
|
||||
{:ok, policy_configs} =
|
||||
policies
|
||||
|
|
@ -82,4 +135,41 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
end
|
||||
|
||||
def describe, do: get_policies() |> describe()
|
||||
|
||||
def config_descriptions do
|
||||
Pleroma.Web.ActivityPub.MRF
|
||||
|> Pleroma.Docs.Generator.list_behaviour_implementations()
|
||||
|> config_descriptions()
|
||||
end
|
||||
|
||||
def config_descriptions(policies) do
|
||||
Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc ->
|
||||
if function_exported?(policy, :config_description, 0) do
|
||||
description =
|
||||
@default_description
|
||||
|> Map.merge(policy.config_description)
|
||||
|> Map.put(:group, :pleroma)
|
||||
|> Map.put(:tab, :mrf)
|
||||
|> Map.put(:type, :group)
|
||||
|
||||
if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do
|
||||
[description | acc]
|
||||
else
|
||||
Logger.warn(
|
||||
"#{policy} config description doesn't have one or all required keys #{
|
||||
inspect(@required_description_keys)
|
||||
}"
|
||||
)
|
||||
|
||||
acc
|
||||
end
|
||||
else
|
||||
Logger.debug(
|
||||
"#{policy} is excluded from config descriptions, because does not implement `config_description/0` method."
|
||||
)
|
||||
|
||||
acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,4 +40,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
|
|||
_ -> Map.put(activity, "expires_at", expires_at)
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_activity_expiration,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
|
||||
label: "MRF Activity Expiration Policy",
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -97,4 +97,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
|||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_hellthread,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
|
||||
label: "MRF Hellthread",
|
||||
description: "Block messages with excessive user mentions",
|
||||
children: [
|
||||
%{
|
||||
key: :delist_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the message gets removed from timelines and" <>
|
||||
"disables notifications. Set to 0 to disable.",
|
||||
suggestions: [10]
|
||||
},
|
||||
%{
|
||||
key: :reject_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
|
||||
suggestions: [20]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -126,4 +126,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
|
||||
{:ok, %{mrf_keyword: mrf_keyword}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_keyword,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
|
||||
label: "MRF Keyword",
|
||||
description:
|
||||
"Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
|
||||
children: [
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being rejected.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :replace,
|
||||
type: {:list, :tuple},
|
||||
description: """
|
||||
**Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
|
||||
**Replacement**: a string. Leaving the field empty is permitted.
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,4 +25,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
|
|||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_mention,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
|
||||
label: "MRF Mention",
|
||||
description: "Block messages which mention a specific user",
|
||||
children: [
|
||||
%{
|
||||
key: :actors,
|
||||
type: {:list, :string},
|
||||
description: "A list of actors for which any post mentioning them will be dropped",
|
||||
suggestions: ["actor1", "actor2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => child_object} = object) do
|
||||
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
|
||||
|
||||
|
|
@ -22,5 +23,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
|
||||
def filter(object), do: {:ok, object}
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_normalize_markup,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
|
||||
label: "MRF Normalize Markup",
|
||||
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
|
||||
children: [
|
||||
%{
|
||||
key: :scrub_policy,
|
||||
type: :module,
|
||||
suggestions: [Pleroma.HTML.Scrubber.Default]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -106,4 +106,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
|||
|
||||
{:ok, %{mrf_object_age: mrf_object_age}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_object_age,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
|
||||
label: "MRF Object Age",
|
||||
description:
|
||||
"Rejects or delists posts based on their timestamp deviance from your server's clock.",
|
||||
children: [
|
||||
%{
|
||||
key: :threshold,
|
||||
type: :integer,
|
||||
description: "Required age (in seconds) of a post before actions are taken.",
|
||||
suggestions: [172_800]
|
||||
},
|
||||
%{
|
||||
key: :actions,
|
||||
type: {:list, :atom},
|
||||
description:
|
||||
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
|
||||
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
|
||||
"`:reject` rejects the message entirely",
|
||||
suggestions: [:delist, :strip_followers, :reject]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,4 +48,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_rejectnonpublic,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
|
||||
description: "RejectNonPublic drops posts with non-public visibility settings.",
|
||||
label: "MRF Reject Non Public",
|
||||
children: [
|
||||
%{
|
||||
key: :allow_followersonly,
|
||||
label: "Allow followers-only",
|
||||
type: :boolean,
|
||||
description: "Whether to allow followers-only posts"
|
||||
},
|
||||
%{
|
||||
key: :allow_direct,
|
||||
type: :boolean,
|
||||
description: "Whether to allow direct messages"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -244,4 +244,78 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
{:ok, %{mrf_simple: mrf_simple}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_simple,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
|
||||
label: "MRF Simple",
|
||||
description: "Simple ingress policies",
|
||||
children: [
|
||||
%{
|
||||
key: :media_removal,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to strip media attachments from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :media_nsfw,
|
||||
label: "Media NSFW",
|
||||
type: {:list, :string},
|
||||
description: "List of instances to tag all media as NSFW (sensitive) from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to reject activities from (except deletes)",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :accept,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to only accept activities from (except deletes)",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :followers_only,
|
||||
type: {:list, :string},
|
||||
description: "Force posts from the given instances to be visible by followers only",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :report_removal,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to reject reports from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :avatar_removal,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to strip avatars from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :banner_removal,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to strip banners from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
key: :reject_deletes,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to reject deletions from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,4 +39,28 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_subchain,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
|
||||
label: "MRF Subchain",
|
||||
description:
|
||||
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
|
||||
" All criteria are configured as a map of regular expressions to lists of policy modules.",
|
||||
children: [
|
||||
%{
|
||||
key: :match_actor,
|
||||
type: {:map, {:list, :string}},
|
||||
description: "Matches a series of regular expressions against the actor field",
|
||||
suggestions: [
|
||||
%{
|
||||
~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,4 +41,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
|||
|
||||
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
|
||||
end
|
||||
|
||||
# TODO: change way of getting settings on `lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex:18` to use `hosts` subkey
|
||||
# @impl true
|
||||
# def config_description do
|
||||
# %{
|
||||
# key: :mrf_user_allowlist,
|
||||
# related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
|
||||
# description: "Accept-list of users from specified instances",
|
||||
# children: [
|
||||
# %{
|
||||
# key: :hosts,
|
||||
# type: :map,
|
||||
# description:
|
||||
# "The keys in this section are the domain names that the policy should apply to." <>
|
||||
# " Each key should be assigned a list of users that should be allowed " <>
|
||||
# "through by their ActivityPub ID",
|
||||
# suggestions: [%{"example.org" => ["https://example.org/users/admin"]}]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Undo", "object" => child_message} = message) do
|
||||
with {:ok, _} <- filter(child_message) do
|
||||
{:ok, message}
|
||||
|
|
@ -36,6 +37,33 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
|||
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_vocabulary,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
|
||||
label: "MRF Vocabulary",
|
||||
description: "Filter messages which belong to certain activity vocabularies",
|
||||
children: [
|
||||
%{
|
||||
key: :accept,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
field(:type, :string)
|
||||
field(:mediaType, :string, default: "application/octet-stream")
|
||||
field(:name, :string)
|
||||
field(:blurhash, :string)
|
||||
|
||||
embeds_many :url, UrlObjectValidator, primary_key: false do
|
||||
field(:type, :string)
|
||||
|
|
@ -41,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
|> fix_url()
|
||||
|
||||
struct
|
||||
|> cast(data, [:type, :mediaType, :name])
|
||||
|> cast(data, [:type, :mediaType, :name, :blurhash])
|
||||
|> cast_embed(:url, with: &url_changeset/2)
|
||||
|> validate_inclusion(:type, ~w[Link Document Audio Image Video])
|
||||
|> validate_required([:type, :mediaType, :url])
|
||||
|
|
|
|||
|
|
@ -242,9 +242,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes an activity to all relevant peers.
|
||||
"""
|
||||
# Publishes an activity to all relevant peers.
|
||||
def publish(%User{} = actor, %Activity{} = activity) do
|
||||
public = is_public?(activity)
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
%User{} = followed <- User.get_cached_by_ap_id(followed_user),
|
||||
{_, {:ok, _}, _, _} <-
|
||||
{:following, User.follow(follower, followed, :follow_pending), follower, followed} do
|
||||
if followed.local && !followed.locked do
|
||||
if followed.local && !followed.is_locked do
|
||||
{:ok, accept_data, _} = Builder.accept(followed, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true)
|
||||
end
|
||||
|
|
@ -187,7 +187,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
||||
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
||||
|
||||
if in_reply_to = object.data["inReplyTo"] do
|
||||
if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
|
||||
Object.increase_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
|
|
@ -306,11 +306,18 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
|
||||
streamables =
|
||||
[[actor, recipient], [recipient, actor]]
|
||||
|> Enum.uniq()
|
||||
|> Enum.map(fn [user, other_user] ->
|
||||
if user.local do
|
||||
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
|
||||
{:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
|
||||
|
||||
Cachex.put(
|
||||
:chat_message_id_idempotency_key_cache,
|
||||
cm_ref.id,
|
||||
meta[:idempotency_key]
|
||||
)
|
||||
|
||||
{
|
||||
["user", "user:pleroma_chat"],
|
||||
{user, %{cm_ref | chat: chat, object: object}}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|> fix_in_reply_to(options)
|
||||
|> fix_emoji
|
||||
|> fix_tag
|
||||
|> set_sensitive
|
||||
|> fix_content_map
|
||||
|> fix_addressing
|
||||
|> fix_summary
|
||||
|
|
@ -251,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
}
|
||||
|> Maps.put_if_present("mediaType", media_type)
|
||||
|> Maps.put_if_present("name", data["name"])
|
||||
|> Maps.put_if_present("blurhash", data["blurhash"])
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
|
@ -313,19 +315,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
tags =
|
||||
tag
|
||||
|> Enum.filter(fn data -> data["type"] == "Hashtag" and data["name"] end)
|
||||
|> Enum.map(fn data -> String.slice(data["name"], 1..-1) end)
|
||||
|> Enum.map(fn %{"name" => name} ->
|
||||
name
|
||||
|> String.slice(1..-1)
|
||||
|> String.downcase()
|
||||
end)
|
||||
|
||||
Map.put(object, "tag", tag ++ tags)
|
||||
end
|
||||
|
||||
def fix_tag(%{"tag" => %{"type" => "Hashtag", "name" => hashtag} = tag} = object) do
|
||||
combined = [tag, String.slice(hashtag, 1..-1)]
|
||||
|
||||
Map.put(object, "tag", combined)
|
||||
def fix_tag(%{"tag" => %{} = tag} = object) do
|
||||
object
|
||||
|> Map.put("tag", [tag])
|
||||
|> fix_tag
|
||||
end
|
||||
|
||||
def fix_tag(%{"tag" => %{} = tag} = object), do: Map.put(object, "tag", [tag])
|
||||
|
||||
def fix_tag(object), do: object
|
||||
|
||||
# content map usually only has one language so this will do for now.
|
||||
|
|
@ -927,7 +931,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
Map.put(object, "conversation", object["context"])
|
||||
end
|
||||
|
||||
def set_sensitive(%{"sensitive" => true} = object) do
|
||||
def set_sensitive(%{"sensitive" => _} = object) do
|
||||
object
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"name" => user.name,
|
||||
"summary" => user.bio,
|
||||
"url" => user.ap_id,
|
||||
"manuallyApprovesFollowers" => user.locked,
|
||||
"manuallyApprovesFollowers" => user.is_locked,
|
||||
"publicKey" => %{
|
||||
"id" => "#{user.ap_id}#main-key",
|
||||
"owner" => user.ap_id,
|
||||
|
|
@ -110,7 +110,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"endpoints" => endpoints,
|
||||
"attachment" => fields,
|
||||
"tag" => emoji_tags,
|
||||
"discoverable" => user.discoverable,
|
||||
"discoverable" => user.is_discoverable,
|
||||
"capabilities" => capabilities
|
||||
}
|
||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||
|
|
|
|||
|
|
@ -44,29 +44,30 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||
def is_list?(_), do: false
|
||||
|
||||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
@spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
|
||||
def visible_for_user?(nil, _), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
||||
def visible_for_user?(
|
||||
%Activity{data: %{"listMessage" => list_ap_id}} = activity,
|
||||
%User{} = user
|
||||
) do
|
||||
user.ap_id in activity.data["to"] ||
|
||||
list_ap_id
|
||||
|> Pleroma.List.get_by_ap_id()
|
||||
|> Pleroma.List.member?(user)
|
||||
end
|
||||
|
||||
def visible_for_user?(%{local: local} = activity, nil) do
|
||||
cfg_key = if local, do: :local, else: :remote
|
||||
|
||||
if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
|
||||
def visible_for_user?(%Activity{} = activity, nil) do
|
||||
if restrict_unauthenticated_access?(activity),
|
||||
do: false,
|
||||
else: is_public?(activity)
|
||||
end
|
||||
|
||||
def visible_for_user?(activity, user) do
|
||||
def visible_for_user?(%Activity{} = activity, user) do
|
||||
x = [user.ap_id | User.following(user)]
|
||||
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
||||
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
||||
|
|
@ -82,6 +83,26 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
result
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%Activity{local: local}) do
|
||||
restrict_unauthenticated_access_to_activity?(local)
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%Object{} = object) do
|
||||
object
|
||||
|> Object.local?()
|
||||
|> restrict_unauthenticated_access_to_activity?()
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%User{} = user) do
|
||||
User.visible_for(user, _reading_user = nil)
|
||||
end
|
||||
|
||||
defp restrict_unauthenticated_access_to_activity?(local?) when is_boolean(local?) do
|
||||
cfg_key = if local?, do: :local, else: :remote
|
||||
|
||||
Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key)
|
||||
end
|
||||
|
||||
def get_visibility(object) do
|
||||
to = object.data["to"] || []
|
||||
cc = object.data["cc"] || []
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [json_response: 3, fetch_integer_param: 3]
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.MFA
|
||||
|
|
@ -13,12 +14,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Router
|
||||
|
|
@ -28,7 +26,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list_users, :user_show, :right_get, :show_user_credentials]
|
||||
when action in [:right_get, :show_user_credentials, :create_backup]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
|
@ -37,12 +35,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
when action in [
|
||||
:get_password_reset,
|
||||
:force_password_reset,
|
||||
:user_delete,
|
||||
:users_create,
|
||||
:user_toggle_activation,
|
||||
:user_activate,
|
||||
:user_deactivate,
|
||||
:user_approve,
|
||||
:tag_users,
|
||||
:untag_users,
|
||||
:right_add,
|
||||
|
|
@ -54,12 +46,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:follows"], admin: true}
|
||||
when action in [:user_follow, :user_unfollow]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"], admin: true}
|
||||
|
|
@ -95,132 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
action_fallback(AdminAPI.FallbackController)
|
||||
|
||||
def user_delete(conn, %{"nickname" => nickname}) do
|
||||
user_delete(conn, %{"nicknames" => [nickname]})
|
||||
end
|
||||
|
||||
def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users =
|
||||
nicknames
|
||||
|> Enum.map(&User.get_cached_by_nickname/1)
|
||||
|
||||
users
|
||||
|> Enum.each(fn user ->
|
||||
{:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
|
||||
Pipeline.common_pipeline(delete_data, local: true)
|
||||
end)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "delete"
|
||||
})
|
||||
|
||||
json(conn, nicknames)
|
||||
end
|
||||
|
||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.follow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "follow"
|
||||
})
|
||||
end
|
||||
|
||||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.unfollow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "unfollow"
|
||||
})
|
||||
end
|
||||
|
||||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||
changesets =
|
||||
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
||||
user_data = %{
|
||||
nickname: nickname,
|
||||
name: nickname,
|
||||
email: email,
|
||||
password: password,
|
||||
password_confirmation: password,
|
||||
bio: "."
|
||||
}
|
||||
|
||||
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
end)
|
||||
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
||||
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
||||
end)
|
||||
|
||||
case Pleroma.Repo.transaction(changesets) do
|
||||
{:ok, users} ->
|
||||
res =
|
||||
users
|
||||
|> Map.values()
|
||||
|> Enum.map(fn user ->
|
||||
{:ok, user} = User.post_register_action(user)
|
||||
|
||||
user
|
||||
end)
|
||||
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subjects: Map.values(users),
|
||||
action: "create"
|
||||
})
|
||||
|
||||
json(conn, res)
|
||||
|
||||
{:error, id, changeset, _} ->
|
||||
res =
|
||||
Enum.map(changesets.operations, fn
|
||||
{current_id, {:changeset, _current_changeset, _}} when current_id == id ->
|
||||
AccountView.render("create-error.json", %{changeset: changeset})
|
||||
|
||||
{_, {:changeset, current_changeset, _}} ->
|
||||
AccountView.render("create-error.json", %{changeset: current_changeset})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> put_status(:conflict)
|
||||
|> json(res)
|
||||
end
|
||||
end
|
||||
|
||||
def user_show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("show.json", %{user: user})
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
||||
{page, page_size} = page_params(params)
|
||||
|
|
@ -274,69 +134,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
{:ok, updated_user} = User.deactivate(user, !user.deactivated)
|
||||
|
||||
action = if user.deactivated, do: "activate", else: "deactivate"
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: [user],
|
||||
action: action
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("show.json", %{user: updated_user})
|
||||
end
|
||||
|
||||
def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.deactivate(users, false)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "activate"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||
end
|
||||
|
||||
def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.deactivate(users, true)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "deactivate"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||
end
|
||||
|
||||
def user_approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.approve(users)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "approve"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: updated_users})
|
||||
end
|
||||
|
||||
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||
ModerationLog.insert_log(%{
|
||||
|
|
@ -363,43 +160,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def list_users(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
filters = maybe_parse_filters(params["filters"])
|
||||
|
||||
search_params = %{
|
||||
query: params["query"],
|
||||
page: page,
|
||||
page_size: page_size,
|
||||
tags: params["tags"],
|
||||
name: params["name"],
|
||||
email: params["email"]
|
||||
}
|
||||
|
||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
|
||||
json(
|
||||
conn,
|
||||
AccountView.render("index.json",
|
||||
users: users,
|
||||
count: count,
|
||||
page_size: page_size
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@filters ~w(local external active deactivated need_approval is_admin is_moderator)
|
||||
|
||||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
||||
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
||||
|
||||
defp maybe_parse_filters(filters) do
|
||||
filters
|
||||
|> String.split(",")
|
||||
|> Enum.filter(&Enum.member?(@filters, &1))
|
||||
|> Map.new(&{String.to_existing_atom(&1), true})
|
||||
end
|
||||
|
||||
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
||||
"permission_group" => permission_group,
|
||||
"nicknames" => nicknames
|
||||
|
|
@ -681,25 +441,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
json(conn, %{"status_visibility" => counters})
|
||||
end
|
||||
|
||||
def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_by_nickname(nickname),
|
||||
{:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do
|
||||
ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
|
||||
|
||||
json(conn, "")
|
||||
end
|
||||
end
|
||||
|
||||
defp page_params(params) do
|
||||
{get_page(params["page"]), get_page_size(params["page_size"])}
|
||||
end
|
||||
|
||||
defp get_page(page_string) when is_nil(page_string), do: 1
|
||||
|
||||
defp get_page(page_string) do
|
||||
case Integer.parse(page_string) do
|
||||
{page, _} -> page
|
||||
:error -> 1
|
||||
end
|
||||
end
|
||||
|
||||
defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
|
||||
|
||||
defp get_page_size(page_size_string) do
|
||||
case Integer.parse(page_size_string) do
|
||||
{page_size, _} -> page_size
|
||||
:error -> @users_page_size
|
||||
end
|
||||
{
|
||||
fetch_integer_param(params, "page", 1),
|
||||
fetch_integer_param(params, "page_size", @users_page_size)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
|||
end
|
||||
|
||||
def show(conn, %{id: id}) do
|
||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
||||
with %Activity{} = report <- Activity.get_report(id) do
|
||||
render(conn, "show.json", Report.extract_report_info(report))
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
|
|
|
|||
281
lib/pleroma/web/admin_api/controllers/user_controller.ex
Normal file
281
lib/pleroma/web/admin_api/controllers/user_controller.ex
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
# 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.UserController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [fetch_integer_param: 3]
|
||||
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
@users_page_size 50
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list, :show]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"], admin: true}
|
||||
when action in [
|
||||
:delete,
|
||||
:create,
|
||||
:toggle_activation,
|
||||
:activate,
|
||||
:deactivate,
|
||||
:approve
|
||||
]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:follows"], admin: true}
|
||||
when action in [:follow, :unfollow]
|
||||
)
|
||||
|
||||
action_fallback(AdminAPI.FallbackController)
|
||||
|
||||
def delete(conn, %{"nickname" => nickname}) do
|
||||
delete(conn, %{"nicknames" => [nickname]})
|
||||
end
|
||||
|
||||
def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
|
||||
Enum.each(users, fn user ->
|
||||
{:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
|
||||
Pipeline.common_pipeline(delete_data, local: true)
|
||||
end)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "delete"
|
||||
})
|
||||
|
||||
json(conn, nicknames)
|
||||
end
|
||||
|
||||
def follow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.follow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "follow"
|
||||
})
|
||||
end
|
||||
|
||||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.unfollow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "unfollow"
|
||||
})
|
||||
end
|
||||
|
||||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||
changesets =
|
||||
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
||||
user_data = %{
|
||||
nickname: nickname,
|
||||
name: nickname,
|
||||
email: email,
|
||||
password: password,
|
||||
password_confirmation: password,
|
||||
bio: "."
|
||||
}
|
||||
|
||||
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
end)
|
||||
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
||||
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
||||
end)
|
||||
|
||||
case Pleroma.Repo.transaction(changesets) do
|
||||
{:ok, users} ->
|
||||
res =
|
||||
users
|
||||
|> Map.values()
|
||||
|> Enum.map(fn user ->
|
||||
{:ok, user} = User.post_register_action(user)
|
||||
|
||||
user
|
||||
end)
|
||||
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subjects: Map.values(users),
|
||||
action: "create"
|
||||
})
|
||||
|
||||
json(conn, res)
|
||||
|
||||
{:error, id, changeset, _} ->
|
||||
res =
|
||||
Enum.map(changesets.operations, fn
|
||||
{current_id, {:changeset, _current_changeset, _}} when current_id == id ->
|
||||
AccountView.render("create-error.json", %{changeset: changeset})
|
||||
|
||||
{_, {:changeset, current_changeset, _}} ->
|
||||
AccountView.render("create-error.json", %{changeset: current_changeset})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> put_status(:conflict)
|
||||
|> json(res)
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("show.json", %{user: user})
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
{:ok, updated_user} = User.deactivate(user, !user.deactivated)
|
||||
|
||||
action = if user.deactivated, do: "activate", else: "deactivate"
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: [user],
|
||||
action: action
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("show.json", %{user: updated_user})
|
||||
end
|
||||
|
||||
def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.deactivate(users, false)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "activate"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||
end
|
||||
|
||||
def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.deactivate(users, true)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "deactivate"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||
end
|
||||
|
||||
def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.approve(users)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "approve"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", %{users: updated_users})
|
||||
end
|
||||
|
||||
def list(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
filters = maybe_parse_filters(params["filters"])
|
||||
|
||||
search_params =
|
||||
%{
|
||||
query: params["query"],
|
||||
page: page,
|
||||
page_size: page_size,
|
||||
tags: params["tags"],
|
||||
name: params["name"],
|
||||
email: params["email"],
|
||||
actor_types: params["actor_types"]
|
||||
}
|
||||
|> Map.merge(filters)
|
||||
|
||||
with {:ok, users, count} <- Search.user(search_params) do
|
||||
json(
|
||||
conn,
|
||||
AccountView.render("index.json",
|
||||
users: users,
|
||||
count: count,
|
||||
page_size: page_size
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@filters ~w(local external active deactivated need_approval unconfirmed is_admin is_moderator)
|
||||
|
||||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
||||
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
||||
|
||||
defp maybe_parse_filters(filters) do
|
||||
filters
|
||||
|> String.split(",")
|
||||
|> Enum.filter(&Enum.member?(@filters, &1))
|
||||
|> Map.new(&{String.to_existing_atom(&1), true})
|
||||
end
|
||||
|
||||
defp page_params(params) do
|
||||
{
|
||||
fetch_integer_param(params, "page", 1),
|
||||
fetch_integer_param(params, "page_size", @users_page_size)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -39,7 +39,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
:fields,
|
||||
:name,
|
||||
:nickname,
|
||||
:locked,
|
||||
:is_locked,
|
||||
:no_rich_text,
|
||||
:default_scope,
|
||||
:hide_follows,
|
||||
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
:skip_thread_containment,
|
||||
:pleroma_settings_store,
|
||||
:raw_fields,
|
||||
:discoverable,
|
||||
:is_discoverable,
|
||||
:actor_type
|
||||
])
|
||||
|> Map.merge(%{
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||
end
|
||||
|
||||
def render("index_notes.json", %{notes: notes}) when is_list(notes) do
|
||||
Enum.map(notes, &render(__MODULE__, "show_note.json", &1))
|
||||
Enum.map(notes, &render(__MODULE__, "show_note.json", Map.from_struct(&1)))
|
||||
end
|
||||
|
||||
def render("index_notes.json", _), do: []
|
||||
|
|
|
|||
|
|
@ -262,6 +262,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
:query,
|
||||
%Schema{allOf: [BooleanLike], default: true},
|
||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||
),
|
||||
Operation.parameter(
|
||||
:expires_in,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 0},
|
||||
"Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
|
|
@ -335,6 +341,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
operationId: "AccountController.mutes",
|
||||
description: "Accounts the user has muted.",
|
||||
security: [%{"oAuth" => ["follow", "read:mutes"]}],
|
||||
parameters: pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
|
|
@ -348,6 +355,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
operationId: "AccountController.blocks",
|
||||
description: "View your blocks. See also accounts/:id/{block,unblock}",
|
||||
security: [%{"oAuth" => ["read:blocks"]}],
|
||||
parameters: pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
|
|
@ -721,10 +729,17 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
nullable: true,
|
||||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||
default: true
|
||||
},
|
||||
expires_in: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"notifications" => true
|
||||
"notifications" => true,
|
||||
"expires_in" => 86_400
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
|||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Chat
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
|
||||
|
||||
|
|
@ -132,7 +133,10 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
|||
tags: ["chat"],
|
||||
summary: "Get a list of chats that you participated in",
|
||||
operationId: "ChatController.index",
|
||||
parameters: pagination_params(),
|
||||
parameters: [
|
||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
||||
| pagination_params()
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("The chats of the user", "application/json", chats_response())
|
||||
},
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
"mention",
|
||||
"pleroma:emoji_reaction",
|
||||
"pleroma:chat_mention",
|
||||
"pleroma:report",
|
||||
"move",
|
||||
"follow_request"
|
||||
],
|
||||
|
|
@ -206,6 +207,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
- `poll` - A poll you have voted in or created has ended
|
||||
- `move` - Someone moved their account
|
||||
- `pleroma:emoji_reaction` - Someone reacted with emoji to your status
|
||||
- `pleroma:chat_mention` - Someone mentioned you in a chat message
|
||||
- `pleroma:report` - Someone was reported
|
||||
"""
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
# 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.PleromaBackupOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def index_operation do
|
||||
%Operation{
|
||||
tags: ["Backups"],
|
||||
summary: "List backups",
|
||||
security: [%{"oAuth" => ["read:account"]}],
|
||||
operationId: "PleromaAPI.BackupController.index",
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"An array of backups",
|
||||
"application/json",
|
||||
%Schema{
|
||||
type: :array,
|
||||
items: backup()
|
||||
}
|
||||
),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["Backups"],
|
||||
summary: "Create a backup",
|
||||
security: [%{"oAuth" => ["read:account"]}],
|
||||
operationId: "PleromaAPI.BackupController.create",
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"An array of backups",
|
||||
"application/json",
|
||||
%Schema{
|
||||
type: :array,
|
||||
items: backup()
|
||||
}
|
||||
),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp backup do
|
||||
%Schema{
|
||||
title: "Backup",
|
||||
description: "Response schema for a backup",
|
||||
type: :object,
|
||||
properties: %{
|
||||
inserted_at: %Schema{type: :string, format: :"date-time"},
|
||||
content_type: %Schema{type: :string},
|
||||
file_name: %Schema{type: :string},
|
||||
file_size: %Schema{type: :integer},
|
||||
processed: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"content_type" => "application/zip",
|
||||
"file_name" =>
|
||||
"https://cofe.fe:4000/media/backups/archive-foobar-20200908T164207-Yr7vuT5Wycv-sN3kSN2iJ0k-9pMo60j9qmvRCdDqIew.zip",
|
||||
"file_size" => 4105,
|
||||
"inserted_at" => "2020-09-08T16:42:07.000Z",
|
||||
"processed" => true
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# 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.PleromaInstancesOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["PleromaInstances"],
|
||||
summary: "Instances federation status",
|
||||
description: "Information about instances deemed unreachable by the server",
|
||||
operationId: "PleromaInstances.show",
|
||||
responses: %{
|
||||
200 => Operation.response("PleromaInstances", "application/json", pleroma_instances())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def pleroma_instances do
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
unreachable: %Schema{
|
||||
type: :object,
|
||||
properties: %{hostname: %Schema{type: :string, format: :"date-time"}}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"unreachable" => %{"consistently-unreachable.name" => "2020-10-14 22:07:58.216473"}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -223,7 +223,27 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
security: [%{"oAuth" => ["write:mutes"]}],
|
||||
description: "Do not receive notifications for the thread that this status is part of.",
|
||||
operationId: "StatusController.mute_conversation",
|
||||
parameters: [id_param()],
|
||||
requestBody:
|
||||
request_body("Parameters", %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
expires_in: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}),
|
||||
parameters: [
|
||||
id_param(),
|
||||
Operation.parameter(
|
||||
:expires_in,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 0},
|
||||
"Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => status_response(),
|
||||
400 => Operation.response("Error", "application/json", ApiError)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [
|
||||
local_param(),
|
||||
instance_param(),
|
||||
only_media_param(),
|
||||
with_muted_param(),
|
||||
exclude_visibilities_param(),
|
||||
|
|
@ -158,8 +159,17 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp instance_param do
|
||||
Operation.parameter(
|
||||
:instance,
|
||||
:query,
|
||||
%Schema{type: :string},
|
||||
"Show only statuses from the given domain"
|
||||
)
|
||||
end
|
||||
|
||||
defp with_muted_param do
|
||||
Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users")
|
||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users")
|
||||
end
|
||||
|
||||
defp exclude_visibilities_param do
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Chat do
|
|||
"fields" => []
|
||||
},
|
||||
"statuses_count" => 1,
|
||||
"locked" => false,
|
||||
"is_locked" => false,
|
||||
"created_at" => "2020-04-16T13:40:15.000Z",
|
||||
"display_name" => "lain",
|
||||
"fields" => [],
|
||||
|
|
|
|||
|
|
@ -28,8 +28,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
|||
},
|
||||
votes_count: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "How many votes have been received. Number, or null if `multiple` is false."
|
||||
description: "How many votes have been received. Number."
|
||||
},
|
||||
voters_count: %Schema{
|
||||
type: :integer,
|
||||
description: "How many unique accounts have voted. Number."
|
||||
},
|
||||
voted: %Schema{
|
||||
type: :boolean,
|
||||
|
|
@ -61,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
|||
expired: true,
|
||||
multiple: false,
|
||||
votes_count: 10,
|
||||
voters_count: nil,
|
||||
voters_count: 10,
|
||||
voted: true,
|
||||
own_votes: [
|
||||
1
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
"header" => "http://localhost:4001/images/banner.png",
|
||||
"header_static" => "http://localhost:4001/images/banner.png",
|
||||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"locked" => false,
|
||||
"is_locked" => false,
|
||||
"note" => "Tester Number 6",
|
||||
"pleroma" => %{
|
||||
"background_image" => nil,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
Pipeline.common_pipeline(create_activity_data,
|
||||
local: true
|
||||
local: true,
|
||||
idempotency_key: opts[:idempotency_key]
|
||||
)} do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
|
@ -453,20 +454,46 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def add_mute(user, activity) do
|
||||
def add_mute(user, activity, params \\ %{}) do
|
||||
expires_in = Map.get(params, :expires_in, 0)
|
||||
|
||||
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]),
|
||||
_ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do
|
||||
if expires_in > 0 do
|
||||
Pleroma.Workers.MuteExpireWorker.enqueue(
|
||||
"unmute_conversation",
|
||||
%{"user_id" => user.id, "activity_id" => activity.id},
|
||||
schedule_in: expires_in
|
||||
)
|
||||
end
|
||||
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
|
||||
end
|
||||
end
|
||||
|
||||
def remove_mute(user, activity) do
|
||||
def remove_mute(%User{} = user, %Activity{} = activity) do
|
||||
ThreadMute.remove_mute(user.id, activity.data["context"])
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
def remove_mute(user_id, activity_id) do
|
||||
with {:user, %User{} = user} <- {:user, User.get_by_id(user_id)},
|
||||
{:activity, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)} do
|
||||
remove_mute(user, activity)
|
||||
else
|
||||
{what, result} = error ->
|
||||
Logger.warn(
|
||||
"CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{
|
||||
activity_id
|
||||
}"
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}})
|
||||
when is_binary(context) do
|
||||
ThreadMute.exists?(user_id, context)
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
def format_input(text, format, options \\ [])
|
||||
|
||||
@doc """
|
||||
Formatting text to plain text.
|
||||
Formatting text to plain text, BBCode, HTML, or Markdown
|
||||
"""
|
||||
def format_input(text, "text/plain", options) do
|
||||
text
|
||||
|
|
@ -285,9 +285,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end).()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Formatting text as BBCode.
|
||||
"""
|
||||
def format_input(text, "text/bbcode", options) do
|
||||
text
|
||||
|> String.replace(~r/\r/, "")
|
||||
|
|
@ -297,18 +294,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|> Formatter.linkify(options)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Formatting text to html.
|
||||
"""
|
||||
def format_input(text, "text/html", options) do
|
||||
text
|
||||
|> Formatter.html_escape("text/html")
|
||||
|> Formatter.linkify(options)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Formatting text to markdown.
|
||||
"""
|
||||
def format_input(text, "text/markdown", options) do
|
||||
text
|
||||
|> Formatter.mentions_escape(options)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Config
|
||||
|
||||
socket("/socket", Pleroma.Web.UserSocket)
|
||||
|
||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||
|
||||
plug(Pleroma.Web.Plugs.SetLocalePlug)
|
||||
plug(CORSPlug)
|
||||
plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
|
||||
|
|
@ -86,19 +90,19 @@ defmodule Pleroma.Web.Endpoint do
|
|||
plug(Plug.Parsers,
|
||||
parsers: [
|
||||
:urlencoded,
|
||||
{:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}},
|
||||
{:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
|
||||
:json
|
||||
],
|
||||
pass: ["*/*"],
|
||||
json_decoder: Jason,
|
||||
length: Pleroma.Config.get([:instance, :upload_limit]),
|
||||
length: Config.get([:instance, :upload_limit]),
|
||||
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
||||
)
|
||||
|
||||
plug(Plug.MethodOverride)
|
||||
plug(Plug.Head)
|
||||
|
||||
secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag])
|
||||
secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
|
||||
|
||||
cookie_name =
|
||||
if secure_cookies,
|
||||
|
|
@ -106,7 +110,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
else: "pleroma_key"
|
||||
|
||||
extra =
|
||||
Pleroma.Config.get([__MODULE__, :extra_cookie_attrs])
|
||||
Config.get([__MODULE__, :extra_cookie_attrs])
|
||||
|> Enum.join(";")
|
||||
|
||||
# The session will be stored in the cookie and signed,
|
||||
|
|
@ -116,7 +120,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
Plug.Session,
|
||||
store: :cookie,
|
||||
key: cookie_name,
|
||||
signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||
signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||
http_only: true,
|
||||
secure: secure_cookies,
|
||||
extra: extra
|
||||
|
|
@ -136,8 +140,34 @@ defmodule Pleroma.Web.Endpoint do
|
|||
use Prometheus.PlugExporter
|
||||
end
|
||||
|
||||
defmodule MetricsExporterCaller do
|
||||
@behaviour Plug
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, opts) do
|
||||
prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
|
||||
ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
|
||||
|
||||
cond do
|
||||
!prometheus_config[:enabled] ->
|
||||
conn
|
||||
|
||||
ip_whitelist != [] and
|
||||
!Enum.find(ip_whitelist, fn ip ->
|
||||
Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
|
||||
end) ->
|
||||
conn
|
||||
|
||||
true ->
|
||||
MetricsExporter.call(conn, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
plug(PipelineInstrumenter)
|
||||
plug(MetricsExporter)
|
||||
|
||||
plug(MetricsExporterCaller)
|
||||
|
||||
plug(Pleroma.Web.Router)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,11 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
|
||||
tags = build_tags(conn, params)
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads)
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads <> title)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
@ -54,10 +55,11 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
def redirector_with_preload(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", preloads)
|
||||
|> String.replace("<!--server-generated-meta-->", preloads <> title)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|
|||
|
||||
def activity_content(_), do: ""
|
||||
|
||||
def activity_context(activity), do: activity.data["context"]
|
||||
def activity_context(activity), do: escape(activity.data["context"])
|
||||
|
||||
def attachment_href(attachment) do
|
||||
attachment["url"]
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
def feed(conn, params) do
|
||||
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do
|
||||
if Config.get!([:instance, :public]) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
render_error(conn, :not_found, "Not found")
|
||||
end
|
||||
end
|
||||
|
||||
def render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
{format, tag} = parse_tag(raw_tag)
|
||||
|
||||
activities =
|
||||
|
|
@ -36,12 +36,13 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
end
|
||||
|
||||
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
||||
defp parse_tag(raw_tag) when is_binary(raw_tag) do
|
||||
case Enum.reverse(String.split(raw_tag, ".")) do
|
||||
[format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")}
|
||||
_ -> {"rss", raw_tag}
|
||||
defp parse_tag(raw_tag) do
|
||||
case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do
|
||||
[format | tag] when format in ["rss", "atom"] ->
|
||||
{format, Enum.join(tag, ".")}
|
||||
|
||||
_ ->
|
||||
{"atom", raw_tag}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_tag(raw_tag), do: {"rss", raw_tag}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.Feed.UserController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||
|
|
@ -22,12 +23,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|
||||
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||
when format in ["json", "activity+json"] do
|
||||
with %{halted: false} = conn <-
|
||||
Pleroma.Web.Plugs.EnsureAuthenticatedPlug.call(conn,
|
||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
||||
) do
|
||||
ActivityPubController.call(conn, :user)
|
||||
end
|
||||
ActivityPubController.call(conn, :user)
|
||||
end
|
||||
|
||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||
|
|
@ -36,25 +32,18 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
end
|
||||
end
|
||||
|
||||
def feed(conn, params) do
|
||||
unless Pleroma.Config.restrict_unauthenticated_access?(:profiles, :local) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
errors(conn, {:error, :not_found})
|
||||
end
|
||||
end
|
||||
|
||||
def render_feed(conn, %{"nickname" => nickname} = params) do
|
||||
def feed(conn, %{"nickname" => nickname} = params) do
|
||||
format = get_format(conn)
|
||||
|
||||
format =
|
||||
if format in ["rss", "atom"] do
|
||||
if format in ["atom", "rss"] do
|
||||
format
|
||||
else
|
||||
"atom"
|
||||
end
|
||||
|
||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)},
|
||||
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||
activities =
|
||||
%{
|
||||
type: ["Create"],
|
||||
|
|
@ -69,7 +58,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|> render("user.#{format}",
|
||||
user: user,
|
||||
activities: activities,
|
||||
feed_config: Pleroma.Config.get([:feed])
|
||||
feed_config: Config.get([:feed])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -81,6 +70,8 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
||||
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||
|
||||
def errors(conn, {:visibility, _}), do: errors(conn, {:error, :not_found})
|
||||
|
||||
def errors(conn, _) do
|
||||
render_error(conn, :internal_server_error, "Something went wrong")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
user_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
:locked,
|
||||
:hide_followers_count,
|
||||
:hide_follows_count,
|
||||
:hide_followers,
|
||||
|
|
@ -186,7 +185,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:allow_following_move,
|
||||
:discoverable,
|
||||
:accepts_chat_messages
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
|
|
@ -210,6 +208,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
|
||||
end)
|
||||
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||
|> Maps.put_if_present(:is_locked, params[:locked])
|
||||
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
|
||||
|
||||
# What happens here:
|
||||
#
|
||||
|
|
@ -394,7 +394,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
|
@ -442,15 +442,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
end
|
||||
|
||||
@doc "GET /api/v1/mutes"
|
||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||
users = User.muted_users(user, _restrict_deactivated = true)
|
||||
render(conn, "index.json", users: users, for: user, as: :user)
|
||||
def mutes(%{assigns: %{user: user}} = conn, params) do
|
||||
users =
|
||||
user
|
||||
|> User.muted_users_relation(_restrict_deactivated = true)
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
|
||||
conn
|
||||
|> add_link_headers(users)
|
||||
|> render("index.json", users: users, for: user, as: :user)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/blocks"
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
users = User.blocked_users(user, _restrict_deactivated = true)
|
||||
render(conn, "index.json", users: users, for: user, as: :user)
|
||||
def blocks(%{assigns: %{user: user}} = conn, params) do
|
||||
users =
|
||||
user
|
||||
|> User.blocked_users_relation(_restrict_deactivated = true)
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
|
||||
conn
|
||||
|> add_link_headers(users)
|
||||
|> render("index.json", users: users, for: user, as: :user)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/endorsements"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
redirect(conn, to: local_mastodon_root_path(conn))
|
||||
end
|
||||
|
||||
@doc "Local Mastodon FE login init action"
|
||||
# Local Mastodon FE login init action
|
||||
def login(conn, %{"code" => auth_token}) do
|
||||
with {:ok, app} <- get_or_make_app(),
|
||||
{:ok, auth} <- Authorization.get_by_token(app, auth_token),
|
||||
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
end
|
||||
end
|
||||
|
||||
@doc "Local Mastodon FE callback action"
|
||||
# Local Mastodon FE callback action
|
||||
def login(conn, _) do
|
||||
with {:ok, app} <- get_or_make_app() do
|
||||
path =
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2])
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||
|
||||
|
|
|
|||
|
|
@ -127,9 +127,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
@doc """
|
||||
POST /api/v1/statuses
|
||||
|
||||
Creates a scheduled status when `scheduled_at` param is present and it's far enough
|
||||
"""
|
||||
# Creates a scheduled status when `scheduled_at` param is present and it's far enough
|
||||
def create(
|
||||
%{
|
||||
assigns: %{user: user},
|
||||
|
|
@ -160,11 +159,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
POST /api/v1/statuses
|
||||
|
||||
Creates a regular status
|
||||
"""
|
||||
# Creates a regular status
|
||||
def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do
|
||||
params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||
|
||||
|
|
@ -289,9 +284,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/mute"
|
||||
def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
def mute_conversation(%{assigns: %{user: user}, body_params: params} = conn, %{id: id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, activity} <- CommonAPI.add_mute(user, activity) do
|
||||
{:ok, activity} <- CommonAPI.add_mute(user, activity, params) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> Map.put(:blocking_user, user)
|
||||
|> Map.put(:muting_user, user)
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> Map.put(:instance, params[:instance])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
conn
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
username: username_from_nickname(user.nickname),
|
||||
acct: user.nickname,
|
||||
display_name: display_name,
|
||||
locked: user.locked,
|
||||
locked: user.is_locked,
|
||||
created_at: Utils.to_masto_date(user.inserted_at),
|
||||
followers_count: followers_count,
|
||||
following_count: following_count,
|
||||
|
|
@ -261,7 +261,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
sensitive: false,
|
||||
fields: user.raw_fields,
|
||||
pleroma: %{
|
||||
discoverable: user.discoverable,
|
||||
discoverable: user.is_discoverable,
|
||||
actor_type: user.actor_type
|
||||
}
|
||||
},
|
||||
|
|
@ -388,7 +388,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
data
|
||||
|> Kernel.put_in(
|
||||
[:pleroma, :unread_conversation_count],
|
||||
user.unread_conversation_count
|
||||
Pleroma.Conversation.Participation.unread_count(user)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,15 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
|||
end
|
||||
|
||||
activity = Activity.get_by_id_with_object(last_activity_id)
|
||||
# Conversations return all users except the current user.
|
||||
users = Enum.reject(participation.recipients, &(&1.id == user.id))
|
||||
|
||||
# Conversations return all users except the current user,
|
||||
# except when the current user is the only participant
|
||||
users =
|
||||
if length(participation.recipients) > 1 do
|
||||
Enum.reject(participation.recipients, &(&1.id == user.id))
|
||||
else
|
||||
participation.recipients
|
||||
end
|
||||
|
||||
%{
|
||||
id: participation.id |> to_string(),
|
||||
|
|
@ -43,7 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
|||
last_status:
|
||||
render(StatusView, "show.json",
|
||||
activity: activity,
|
||||
direct_conversation_id: participation.id
|
||||
direct_conversation_id: participation.id,
|
||||
for: user
|
||||
)
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.AdminAPI.Report
|
||||
alias Pleroma.Web.AdminAPI.ReportView
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||
|
|
@ -118,11 +120,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
"pleroma:chat_mention" ->
|
||||
put_chat_message(response, activity, reading_user, status_render_opts)
|
||||
|
||||
"pleroma:report" ->
|
||||
put_report(response, activity)
|
||||
|
||||
type when type in ["follow", "follow_request"] ->
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
defp put_report(response, activity) do
|
||||
report_render = ReportView.render("show.json", Report.extract_report_info(activity))
|
||||
|
||||
Map.put(response, :report, report_render)
|
||||
end
|
||||
|
||||
defp put_emoji(response, activity) do
|
||||
Map.put(response, :emoji, activity.data["content"])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
|||
expired: expired,
|
||||
multiple: multiple,
|
||||
votes_count: votes_count,
|
||||
voters_count: (multiple || nil) && voters_count(object),
|
||||
voters_count: voters_count(object),
|
||||
options: options,
|
||||
voted: voted?(params),
|
||||
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
|
||||
|
|
|
|||
|
|
@ -435,7 +435,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
text_url: href,
|
||||
type: type,
|
||||
description: attachment["name"],
|
||||
pleroma: %{mime_type: media_type}
|
||||
pleroma: %{mime_type: media_type},
|
||||
blurhash: attachment["blurhash"]
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.Http do
|
|||
{:ok, %{status: status} = env} when 400 <= status and status < 500 ->
|
||||
{:error, env}
|
||||
|
||||
{:error, error} = error ->
|
||||
{:error, _} = error ->
|
||||
error
|
||||
|
||||
_ ->
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do
|
|||
"""
|
||||
|
||||
@impl true
|
||||
def build_tags(%{user: %{local: true, discoverable: true}}), do: []
|
||||
def build_tags(%{user: %{local: true, is_discoverable: true}}), do: []
|
||||
|
||||
def build_tags(_) do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
||||
|
|
@ -37,14 +33,12 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
ActivityPubController.call(conn, :object)
|
||||
end
|
||||
|
||||
def object(%{assigns: %{format: format}} = conn, _params) do
|
||||
def object(conn, _params) do
|
||||
with id <- Endpoint.url() <> conn.request_path,
|
||||
{_, %Activity{} = activity} <-
|
||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
redirect(conn, to: "/notice/#{activity.id}")
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
{:error, :not_found}
|
||||
|
|
@ -59,13 +53,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
ActivityPubController.call(conn, :activity)
|
||||
end
|
||||
|
||||
def activity(%{assigns: %{format: format}} = conn, _params) do
|
||||
def activity(conn, _params) do
|
||||
with id <- Endpoint.url() <> conn.request_path,
|
||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
redirect(conn, to: "/notice/#{activity.id}")
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
{:error, :not_found}
|
||||
|
|
@ -119,6 +111,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
def notice_player(conn, %{"id" => id}) do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.is_public?(activity),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%Object{} = object <- Object.normalize(activity),
|
||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
plug(
|
||||
Majic.Plug,
|
||||
[pool: Pleroma.MajicPool] when action in [:update_avatar, :update_background, :update_banner]
|
||||
)
|
||||
|
||||
plug(
|
||||
OpenApiSpex.Plug.PutApiSpec,
|
||||
[module: Pleroma.Web.ApiSpec] when action == :confirmation_resend
|
||||
|
|
|
|||
28
lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
Normal file
28
lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.BackupController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User.Backup
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create])
|
||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation
|
||||
|
||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||
backups = Backup.list(user)
|
||||
render(conn, "index.json", backups: backups)
|
||||
end
|
||||
|
||||
def create(%{assigns: %{user: user}} = conn, _params) do
|
||||
with {:ok, _} <- Backup.create(user) do
|
||||
backups = Backup.list(user)
|
||||
render(conn, "index.json", backups: backups)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15,7 +15,6 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
||||
alias Pleroma.Web.PleromaAPI.ChatView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
import Ecto.Query
|
||||
|
|
@ -80,7 +79,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
|
||||
{:ok, activity} <-
|
||||
CommonAPI.post_chat_message(user, recipient, params[:content],
|
||||
media_id: params[:media_id]
|
||||
media_id: params[:media_id],
|
||||
idempotency_key: idempotency_key(conn)
|
||||
),
|
||||
message <- Object.normalize(activity, false),
|
||||
cm_ref <- MessageReference.for_chat_and_object(chat, message) do
|
||||
|
|
@ -120,9 +120,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
) do
|
||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
|
||||
{_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -140,33 +138,37 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
end
|
||||
end
|
||||
|
||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
|
||||
blocked_ap_ids = User.blocked_users_ap_ids(user)
|
||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do
|
||||
exclude_users =
|
||||
User.blocked_users_ap_ids(user) ++
|
||||
if params[:with_muted], do: [], else: User.muted_users_ap_ids(user)
|
||||
|
||||
chats =
|
||||
Chat.for_user_query(user_id)
|
||||
|> where([c], c.recipient not in ^blocked_ap_ids)
|
||||
user_id
|
||||
|> Chat.for_user_query()
|
||||
|> where([c], c.recipient not in ^exclude_users)
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("index.json", chats: chats)
|
||||
render(conn, "index.json", chats: chats)
|
||||
end
|
||||
|
||||
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with %User{ap_id: recipient} <- User.get_cached_by_id(id),
|
||||
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
defp idempotency_key(conn) do
|
||||
case get_req_header(conn, "idempotency-key") do
|
||||
[key] -> key
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.InstancesController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Instances
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesOperation
|
||||
|
||||
def show(conn, _params) do
|
||||
unreachable =
|
||||
Instances.get_consistently_unreachable()
|
||||
|> Map.new(fn {host, date} -> {host, to_string(date)} end)
|
||||
|
||||
json(conn, %{"unreachable" => unreachable})
|
||||
end
|
||||
end
|
||||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
|
|||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:update])
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show)
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show)
|
||||
|
|
@ -22,14 +23,15 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
|
|||
|
||||
@doc "PUT /api/v1/pleroma/mascot"
|
||||
def update(%{assigns: %{user: user}, body_params: %{file: file}} = conn, _) do
|
||||
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
||||
# Reject if not an image
|
||||
%{type: "image"} = attachment <- render_attachment(object) do
|
||||
with {:content_type, "image" <> _} <- {:content_type, file.content_type},
|
||||
{:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)) do
|
||||
attachment = render_attachment(object)
|
||||
{:ok, _user} = User.mascot_update(user, attachment)
|
||||
|
||||
json(conn, attachment)
|
||||
else
|
||||
%{type: _} -> render_error(conn, :unsupported_media_type, "mascots can only be images")
|
||||
{:content_type, _} ->
|
||||
render_error(conn, :unsupported_media_type, "mascots can only be images")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
28
lib/pleroma/web/pleroma_api/views/backup_view.ex
Normal file
28
lib/pleroma/web/pleroma_api/views/backup_view.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.BackupView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.User.Backup
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
def render("show.json", %{backup: %Backup{} = backup}) do
|
||||
%{
|
||||
content_type: backup.content_type,
|
||||
url: download_url(backup),
|
||||
file_size: backup.file_size,
|
||||
processed: backup.processed,
|
||||
inserted_at: Utils.to_masto_date(backup.inserted_at)
|
||||
}
|
||||
end
|
||||
|
||||
def render("index.json", %{backups: backups}) do
|
||||
render_many(backups, __MODULE__, "show.json")
|
||||
end
|
||||
|
||||
def download_url(%Backup{file_name: file_name}) do
|
||||
Pleroma.Web.Endpoint.url() <> "/media/backups/" <> file_name
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
|
@ -37,6 +38,7 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
|||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object)
|
||||
)
|
||||
}
|
||||
|> put_idempotency_key()
|
||||
end
|
||||
|
||||
def render("index.json", opts) do
|
||||
|
|
@ -47,4 +49,13 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
|||
Map.put(opts, :as, :chat_message_reference)
|
||||
)
|
||||
end
|
||||
|
||||
defp put_idempotency_key(data) do
|
||||
with {:ok, idempotency_key} <- Cachex.get(:chat_message_id_idempotency_key_cache, data.id) do
|
||||
data
|
||||
|> Maps.put_if_present(:idempotency_key, idempotency_key)
|
||||
else
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,22 +34,26 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
|||
end
|
||||
|
||||
def call(conn, opts) do
|
||||
frontend_type = Map.get(opts, :frontend_type, :primary)
|
||||
path = file_path("", frontend_type)
|
||||
|
||||
if path do
|
||||
conn
|
||||
|> call_static(opts, path)
|
||||
with false <- invalid_path?(conn.path_info),
|
||||
frontend_type <- Map.get(opts, :frontend_type, :primary),
|
||||
path when not is_nil(path) <- file_path("", frontend_type) do
|
||||
call_static(conn, opts, path)
|
||||
else
|
||||
conn
|
||||
_ ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp call_static(conn, opts, from) do
|
||||
opts =
|
||||
opts
|
||||
|> Map.put(:from, from)
|
||||
defp invalid_path?(list) do
|
||||
invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
|
||||
end
|
||||
|
||||
defp invalid_path?([h | _], _match) when h in [".", "..", ""], do: true
|
||||
defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t)
|
||||
defp invalid_path?([], _match), do: false
|
||||
|
||||
defp call_static(conn, opts, from) do
|
||||
opts = Map.put(opts, :from, from)
|
||||
Plug.Static.call(conn, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,26 @@
|
|||
defmodule Pleroma.Web.Router do
|
||||
use Pleroma.Web, :router
|
||||
|
||||
pipeline :accepts_html do
|
||||
plug(:accepts, ["html"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_xml do
|
||||
plug(:accepts, ["html", "xml", "rss", "atom"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_json do
|
||||
plug(:accepts, ["html", "activity+json", "json"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_xml_json do
|
||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||
end
|
||||
|
||||
pipeline :accepts_xml_rss_atom do
|
||||
plug(:accepts, ["xml", "rss", "atom"])
|
||||
end
|
||||
|
||||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
|
|
@ -129,16 +149,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through(:admin_api)
|
||||
|
||||
post("/users/follow", AdminAPIController, :user_follow)
|
||||
post("/users/unfollow", AdminAPIController, :user_unfollow)
|
||||
|
||||
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
||||
delete("/users", AdminAPIController, :user_delete)
|
||||
post("/users", AdminAPIController, :users_create)
|
||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||
patch("/users/activate", AdminAPIController, :user_activate)
|
||||
patch("/users/deactivate", AdminAPIController, :user_deactivate)
|
||||
patch("/users/approve", AdminAPIController, :user_approve)
|
||||
put("/users/tag", AdminAPIController, :tag_users)
|
||||
delete("/users/tag", AdminAPIController, :untag_users)
|
||||
|
||||
|
|
@ -161,6 +172,15 @@ defmodule Pleroma.Web.Router do
|
|||
:right_delete_multiple
|
||||
)
|
||||
|
||||
post("/users/follow", UserController, :follow)
|
||||
post("/users/unfollow", UserController, :unfollow)
|
||||
delete("/users", UserController, :delete)
|
||||
post("/users", UserController, :create)
|
||||
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
||||
patch("/users/activate", UserController, :activate)
|
||||
patch("/users/deactivate", UserController, :deactivate)
|
||||
patch("/users/approve", UserController, :approve)
|
||||
|
||||
get("/relay", RelayController, :index)
|
||||
post("/relay", RelayController, :follow)
|
||||
delete("/relay", RelayController, :unfollow)
|
||||
|
|
@ -175,8 +195,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||
|
||||
get("/users", AdminAPIController, :list_users)
|
||||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
get("/users", UserController, :list)
|
||||
get("/users/:nickname", UserController, :show)
|
||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||
|
||||
|
|
@ -223,6 +243,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/chats/:id", ChatController, :show)
|
||||
get("/chats/:id/messages", ChatController, :messages)
|
||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||
|
||||
post("/backups", AdminAPIController, :create_backup)
|
||||
end
|
||||
|
||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||
|
|
@ -353,6 +375,9 @@ defmodule Pleroma.Web.Router do
|
|||
put("/mascot", MascotController, :update)
|
||||
|
||||
post("/scrobble", ScrobbleController, :create)
|
||||
|
||||
get("/backups", BackupController, :index)
|
||||
post("/backups", BackupController, :create)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
@ -373,6 +398,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:api)
|
||||
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
||||
get("/federation_status", InstancesController, :show)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
|
|
@ -566,30 +592,43 @@ defmodule Pleroma.Web.Router do
|
|||
)
|
||||
end
|
||||
|
||||
pipeline :ostatus do
|
||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
||||
end
|
||||
|
||||
pipeline :oembed do
|
||||
plug(:accepts, ["json", "xml"])
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through([:ostatus, :http_signature])
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||
pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||
|
||||
# Mastodon compatibility routes
|
||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||
pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
|
||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:accepts_html)
|
||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:accepts_xml_rss_atom)
|
||||
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
||||
plug(:assign_id)
|
||||
|
||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
||||
)
|
||||
|
||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||
|
||||
@doc "Renders requested local public activity or public activities of requested user"
|
||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||
with %Activity{local: true} = activity <-
|
||||
Activity.get_by_id_with_object(notice_id),
|
||||
true <- Visibility.is_public?(activity.object),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||
|
||||
timeline =
|
||||
activity.object.data["context"]
|
||||
|> ActivityPub.fetch_activities_for_context(%{})
|
||||
|> Enum.reverse()
|
||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||
|
||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||
else
|
||||
%Activity{object: %Object{data: data}} ->
|
||||
conn
|
||||
|> put_status(:found)
|
||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||
with {_, %User{local: true} = user} <-
|
||||
{:fetch_user, User.get_cached_by_nickname_or_id(username_or_id)},
|
||||
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||
meta = Metadata.build_tags(%{user: user})
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.take(@page_keys)
|
||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||
|
||||
timeline =
|
||||
user
|
||||
|> ActivityPub.fetch_user_activities(_reading_user = nil, params)
|
||||
|> Enum.map(&represent/1)
|
||||
|
||||
prev_page_id =
|
||||
(params["min_id"] || params["max_id"]) &&
|
||||
List.first(timeline) && List.first(timeline).id
|
||||
|
||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||
|
||||
render(conn, "profile.html", %{
|
||||
user: User.sanitize_html(user),
|
||||
timeline: timeline,
|
||||
prev_page_id: prev_page_id,
|
||||
next_page_id: next_page_id,
|
||||
meta: meta
|
||||
})
|
||||
else
|
||||
_ ->
|
||||
not_found(conn, "User not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_by_ap_id(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
||||
do: name
|
||||
|
||||
|
|
@ -81,91 +165,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
}
|
||||
end
|
||||
|
||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||
with %Activity{local: true} = activity <-
|
||||
Activity.get_by_id_with_object(notice_id),
|
||||
true <- Visibility.is_public?(activity.object),
|
||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||
|
||||
timeline =
|
||||
activity.object.data["context"]
|
||||
|> ActivityPub.fetch_activities_for_context(%{})
|
||||
|> Enum.reverse()
|
||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||
|
||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||
else
|
||||
%Activity{object: %Object{data: data}} ->
|
||||
conn
|
||||
|> put_status(:found)
|
||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||
case User.get_cached_by_nickname_or_id(username_or_id) do
|
||||
%User{} = user ->
|
||||
meta = Metadata.build_tags(%{user: user})
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.take(@page_keys)
|
||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||
|
||||
timeline =
|
||||
user
|
||||
|> ActivityPub.fetch_user_activities(nil, params)
|
||||
|> Enum.map(&represent/1)
|
||||
|
||||
prev_page_id =
|
||||
(params["min_id"] || params["max_id"]) &&
|
||||
List.first(timeline) && List.first(timeline).id
|
||||
|
||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||
|
||||
render(conn, "profile.html", %{
|
||||
user: User.sanitize_html(user),
|
||||
timeline: timeline,
|
||||
prev_page_id: prev_page_id,
|
||||
next_page_id: next_page_id,
|
||||
meta: meta
|
||||
})
|
||||
|
||||
_ ->
|
||||
not_found(conn, "User not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_by_ap_id(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ defmodule Pleroma.Web.Streamer do
|
|||
{:ok, "hashtag:" <> tag}
|
||||
end
|
||||
|
||||
# Allow remote instance streams.
|
||||
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:" <> instance}
|
||||
end
|
||||
|
||||
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:media:" <> instance}
|
||||
end
|
||||
|
||||
# Expand user streams.
|
||||
def get_topic(
|
||||
stream,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
|
||||
|
||||
<%= if @data["summary"] do %>
|
||||
<summary><%= @data["summary"] %></summary>
|
||||
<summary><%= escape(@data["summary"]) %></summary>
|
||||
<% end %>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
|
||||
|
||||
<%= if @data["summary"] do %>
|
||||
<description><%= @data["summary"] %></description>
|
||||
<description><%= escape(@data["summary"]) %></description>
|
||||
<% end %>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@
|
|||
<body>
|
||||
<div class="container">
|
||||
<h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
<%= @inner_content %>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ video, audio {
|
|||
}
|
||||
</style>
|
||||
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
<%= @inner_content %>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
54
lib/pleroma/workers/backup_worker.ex
Normal file
54
lib/pleroma/workers/backup_worker.ex
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.BackupWorker do
|
||||
use Oban.Worker, queue: :backup, max_attempts: 1
|
||||
|
||||
alias Oban.Job
|
||||
alias Pleroma.User.Backup
|
||||
|
||||
def process(backup, admin_user_id \\ nil) do
|
||||
%{"op" => "process", "backup_id" => backup.id, "admin_user_id" => admin_user_id}
|
||||
|> new()
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
def schedule_deletion(backup) do
|
||||
days = Pleroma.Config.get([Backup, :purge_after_days])
|
||||
time = 60 * 60 * 24 * days
|
||||
scheduled_at = Calendar.NaiveDateTime.add!(backup.inserted_at, time)
|
||||
|
||||
%{"op" => "delete", "backup_id" => backup.id}
|
||||
|> new(scheduled_at: scheduled_at)
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
def delete(backup) do
|
||||
%{"op" => "delete", "backup_id" => backup.id}
|
||||
|> new()
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
def perform(%Job{
|
||||
args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id}
|
||||
}) do
|
||||
with {:ok, %Backup{} = backup} <-
|
||||
backup_id |> Backup.get() |> Backup.process(),
|
||||
{:ok, _job} <- schedule_deletion(backup),
|
||||
:ok <- Backup.remove_outdated(backup),
|
||||
{:ok, _} <-
|
||||
backup
|
||||
|> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
|
||||
|> Pleroma.Emails.Mailer.deliver() do
|
||||
{:ok, backup}
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "delete", "backup_id" => backup_id}}) do
|
||||
case Backup.get(backup_id) do
|
||||
%Backup{} = backup -> Backup.delete(backup)
|
||||
nil -> :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue