forked from AkkomaGang/akkoma
Merge branch 'develop' into openapi/admin/config
This commit is contained in:
commit
b4d5bdd6f1
68 changed files with 3112 additions and 1503 deletions
|
@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Fix follower/blocks import when nicknames starts with @
|
- Fix follower/blocks import when nicknames starts with @
|
||||||
- Filtering of push notifications on activities from blocked domains
|
- Filtering of push notifications on activities from blocked domains
|
||||||
- Resolving Peertube accounts with Webfinger
|
- Resolving Peertube accounts with Webfinger
|
||||||
|
- `blob:` urls not being allowed by connect-src CSP
|
||||||
|
|
||||||
## [Unreleased (patch)]
|
## [Unreleased (patch)]
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,21 @@ defmodule Pleroma.LoadTesting.Activities do
|
||||||
@max_concurrency 10
|
@max_concurrency 10
|
||||||
|
|
||||||
@visibility ~w(public private direct unlisted)
|
@visibility ~w(public private direct unlisted)
|
||||||
@types ~w(simple emoji mentions hell_thread attachment tag like reblog simple_thread remote)
|
@types [
|
||||||
@groups ~w(user friends non_friends)
|
:simple,
|
||||||
|
:emoji,
|
||||||
|
:mentions,
|
||||||
|
:hell_thread,
|
||||||
|
:attachment,
|
||||||
|
:tag,
|
||||||
|
:like,
|
||||||
|
:reblog,
|
||||||
|
:simple_thread
|
||||||
|
]
|
||||||
|
@groups [:friends_local, :friends_remote, :non_friends_local, :non_friends_local]
|
||||||
|
@remote_groups [:friends_remote, :non_friends_remote]
|
||||||
|
@friends_groups [:friends_local, :friends_remote]
|
||||||
|
@non_friends_groups [:non_friends_local, :non_friends_remote]
|
||||||
|
|
||||||
@spec generate(User.t(), keyword()) :: :ok
|
@spec generate(User.t(), keyword()) :: :ok
|
||||||
def generate(user, opts \\ []) do
|
def generate(user, opts \\ []) do
|
||||||
|
@ -34,33 +47,24 @@ def generate(user, opts \\ []) do
|
||||||
|
|
||||||
opts = Keyword.merge(@defaults, opts)
|
opts = Keyword.merge(@defaults, opts)
|
||||||
|
|
||||||
friends =
|
users = Users.prepare_users(user, opts)
|
||||||
user
|
|
||||||
|> Users.get_users(limit: opts[:friends_used], local: :local, friends?: true)
|
|
||||||
|> Enum.shuffle()
|
|
||||||
|
|
||||||
non_friends =
|
{:ok, _} = Agent.start_link(fn -> users[:non_friends_remote] end, name: :non_friends_remote)
|
||||||
user
|
|
||||||
|> Users.get_users(limit: opts[:non_friends_used], local: :local, friends?: false)
|
|
||||||
|> Enum.shuffle()
|
|
||||||
|
|
||||||
task_data =
|
task_data =
|
||||||
for visibility <- @visibility,
|
for visibility <- @visibility,
|
||||||
type <- @types,
|
type <- @types,
|
||||||
group <- @groups,
|
group <- [:user | @groups],
|
||||||
do: {visibility, type, group}
|
do: {visibility, type, group}
|
||||||
|
|
||||||
IO.puts("Starting generating #{opts[:iterations]} iterations of activities...")
|
IO.puts("Starting generating #{opts[:iterations]} iterations of activities...")
|
||||||
|
|
||||||
friends_thread = Enum.take(friends, 5)
|
|
||||||
non_friends_thread = Enum.take(friends, 5)
|
|
||||||
|
|
||||||
public_long_thread = fn ->
|
public_long_thread = fn ->
|
||||||
generate_long_thread("public", user, friends_thread, non_friends_thread, opts)
|
generate_long_thread("public", users, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
private_long_thread = fn ->
|
private_long_thread = fn ->
|
||||||
generate_long_thread("private", user, friends_thread, non_friends_thread, opts)
|
generate_long_thread("private", users, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
iterations = opts[:iterations]
|
iterations = opts[:iterations]
|
||||||
|
@ -73,10 +77,10 @@ def generate(user, opts \\ []) do
|
||||||
i when i == iterations - 2 ->
|
i when i == iterations - 2 ->
|
||||||
spawn(public_long_thread)
|
spawn(public_long_thread)
|
||||||
spawn(private_long_thread)
|
spawn(private_long_thread)
|
||||||
generate_activities(user, friends, non_friends, Enum.shuffle(task_data), opts)
|
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
generate_activities(user, friends, non_friends, Enum.shuffle(task_data), opts)
|
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
@ -127,16 +131,16 @@ def generate_tagged_activities(opts \\ []) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_long_thread(visibility, user, friends, non_friends, _opts) do
|
defp generate_long_thread(visibility, users, _opts) do
|
||||||
group =
|
group =
|
||||||
if visibility == "public",
|
if visibility == "public",
|
||||||
do: "friends",
|
do: :friends_local,
|
||||||
else: "user"
|
else: :user
|
||||||
|
|
||||||
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
|
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
CommonAPI.post(user, %{
|
CommonAPI.post(users[:user], %{
|
||||||
status: "Start of #{visibility} long thread",
|
status: "Start of #{visibility} long thread",
|
||||||
visibility: visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
|
@ -150,31 +154,28 @@ defp generate_long_thread(visibility, user, friends, non_friends, _opts) do
|
||||||
Map.put(state, key, activity)
|
Map.put(state, key, activity)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> user.nickname, "reply to long thread"]}
|
acc = {activity.id, ["@" <> users[:user].nickname, "reply to long thread"]}
|
||||||
insert_replies_for_long_thread(tasks, visibility, user, friends, non_friends, acc)
|
insert_replies_for_long_thread(tasks, visibility, users, acc)
|
||||||
IO.puts("Generating #{visibility} long thread ended\n")
|
IO.puts("Generating #{visibility} long thread ended\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_replies_for_long_thread(tasks, visibility, user, friends, non_friends, acc) do
|
defp insert_replies_for_long_thread(tasks, visibility, users, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
"friend", {id, data} ->
|
:user, {id, data} ->
|
||||||
friend = Enum.random(friends)
|
user = users[:user]
|
||||||
insert_reply(friend, List.delete(data, "@" <> friend.nickname), id, visibility)
|
|
||||||
|
|
||||||
"non_friend", {id, data} ->
|
|
||||||
non_friend = Enum.random(non_friends)
|
|
||||||
insert_reply(non_friend, List.delete(data, "@" <> non_friend.nickname), id, visibility)
|
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
insert_reply(user, List.delete(data, "@" <> user.nickname), id, visibility)
|
insert_reply(user, List.delete(data, "@" <> user.nickname), id, visibility)
|
||||||
|
|
||||||
|
group, {id, data} ->
|
||||||
|
replier = Enum.random(users[group])
|
||||||
|
insert_reply(replier, List.delete(data, "@" <> replier.nickname), id, visibility)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_activities(user, friends, non_friends, task_data, opts) do
|
defp generate_activities(users, task_data, opts) do
|
||||||
Task.async_stream(
|
Task.async_stream(
|
||||||
task_data,
|
task_data,
|
||||||
fn {visibility, type, group} ->
|
fn {visibility, type, group} ->
|
||||||
insert_activity(type, visibility, group, user, friends, non_friends, opts)
|
insert_activity(type, visibility, group, users, opts)
|
||||||
end,
|
end,
|
||||||
max_concurrency: @max_concurrency,
|
max_concurrency: @max_concurrency,
|
||||||
timeout: 30_000
|
timeout: 30_000
|
||||||
|
@ -182,47 +183,46 @@ defp generate_activities(user, friends, non_friends, task_data, opts) do
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("simple", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_local_activity(visibility, group, users, status) do
|
||||||
{:ok, _activity} =
|
{:ok, _} =
|
||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(users)
|
||||||
|> CommonAPI.post(%{status: "Simple status", visibility: visibility})
|
|> CommonAPI.post(%{status: status, visibility: visibility})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_remote_activity(visibility, group, users, status) do
|
||||||
{:ok, _activity} =
|
actor = get_actor(group, users)
|
||||||
group
|
{act_data, obj_data} = prepare_activity_data(actor, visibility, users[:user])
|
||||||
|> get_actor(user, friends, non_friends)
|
{activity_data, object_data} = other_data(actor, status)
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: "Simple status with emoji :firefox:",
|
activity_data
|
||||||
visibility: visibility
|
|> Map.merge(act_data)
|
||||||
})
|
|> Map.put("object", Map.merge(object_data, obj_data))
|
||||||
|
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("mentions", visibility, group, user, friends, non_friends, _opts) do
|
defp user_mentions(users) do
|
||||||
user_mentions =
|
user_mentions =
|
||||||
get_random_mentions(friends, Enum.random(0..3)) ++
|
Enum.reduce(
|
||||||
get_random_mentions(non_friends, Enum.random(0..3))
|
@groups,
|
||||||
|
[],
|
||||||
|
fn group, acc ->
|
||||||
|
acc ++ get_random_mentions(users[group], Enum.random(0..2))
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
user_mentions =
|
|
||||||
if Enum.random([true, false]),
|
if Enum.random([true, false]),
|
||||||
do: ["@" <> user.nickname | user_mentions],
|
do: ["@" <> users[:user].nickname | user_mentions],
|
||||||
else: user_mentions
|
else: user_mentions
|
||||||
|
|
||||||
{:ok, _activity} =
|
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: Enum.join(user_mentions, ", ") <> " simple status with mentions",
|
|
||||||
visibility: visibility
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("hell_thread", visibility, group, user, friends, non_friends, _opts) do
|
defp hell_thread_mentions(users) do
|
||||||
mentions =
|
|
||||||
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
|
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
|
||||||
cached =
|
cached =
|
||||||
([user | Enum.take(friends, 10)] ++ Enum.take(non_friends, 10))
|
@groups
|
||||||
|
|> Enum.reduce([users[:user]], fn group, acc ->
|
||||||
|
acc ++ Enum.take(users[group], 5)
|
||||||
|
end)
|
||||||
|> Enum.map(&"@#{&1.nickname}")
|
|> Enum.map(&"@#{&1.nickname}")
|
||||||
|> Enum.join(", ")
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
@ -231,18 +231,56 @@ defp insert_activity("hell_thread", visibility, group, user, friends, non_friend
|
||||||
else
|
else
|
||||||
{:ok, cached} -> cached
|
{:ok, cached} -> cached
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, _activity} =
|
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: mentions <> " hell thread status",
|
|
||||||
visibility: visibility
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("attachment", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_activity(:simple, visibility, group, users, _opts)
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
when group in @remote_groups do
|
||||||
|
insert_remote_activity(visibility, group, users, "Remote status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:simple, visibility, group, users, _opts) do
|
||||||
|
insert_local_activity(visibility, group, users, "Simple status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:emoji, visibility, group, users, _opts)
|
||||||
|
when group in @remote_groups do
|
||||||
|
insert_remote_activity(visibility, group, users, "Remote status with emoji :firefox:")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:emoji, visibility, group, users, _opts) do
|
||||||
|
insert_local_activity(visibility, group, users, "Simple status with emoji :firefox:")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:mentions, visibility, group, users, _opts)
|
||||||
|
when group in @remote_groups do
|
||||||
|
mentions = user_mentions(users)
|
||||||
|
|
||||||
|
status = Enum.join(mentions, ", ") <> " remote status with mentions"
|
||||||
|
|
||||||
|
insert_remote_activity(visibility, group, users, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:mentions, visibility, group, users, _opts) do
|
||||||
|
mentions = user_mentions(users)
|
||||||
|
|
||||||
|
status = Enum.join(mentions, ", ") <> " simple status with mentions"
|
||||||
|
insert_remote_activity(visibility, group, users, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:hell_thread, visibility, group, users, _)
|
||||||
|
when group in @remote_groups do
|
||||||
|
mentions = hell_thread_mentions(users)
|
||||||
|
insert_remote_activity(visibility, group, users, mentions <> " remote hell thread status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:hell_thread, visibility, group, users, _opts) do
|
||||||
|
mentions = hell_thread_mentions(users)
|
||||||
|
|
||||||
|
insert_local_activity(visibility, group, users, mentions <> " hell thread status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:attachment, visibility, group, users, _opts) do
|
||||||
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
obj_data = %{
|
obj_data = %{
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
|
@ -268,67 +306,54 @@ defp insert_activity("attachment", visibility, group, user, friends, non_friends
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("tag", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_activity(:tag, visibility, group, users, _opts) do
|
||||||
{:ok, _activity} =
|
insert_local_activity(visibility, group, users, "Status with #tag")
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{status: "Status with #tag", visibility: visibility})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do
|
defp insert_activity(:like, visibility, group, users, opts) do
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||||
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, _} ->
|
{:error, _} ->
|
||||||
insert_activity("like", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:like, visibility, group, users, opts)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
Process.sleep(15)
|
Process.sleep(15)
|
||||||
insert_activity("like", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:like, visibility, group, users, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("reblog", visibility, group, user, friends, non_friends, opts) do
|
defp insert_activity(:reblog, visibility, group, users, opts) do
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||||
{:ok, _activity, _object} <- CommonAPI.repeat(activity_id, actor) do
|
{:ok, _activity} <- CommonAPI.repeat(activity_id, actor) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, _} ->
|
{:error, _} ->
|
||||||
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:reblog, visibility, group, users, opts)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
Process.sleep(15)
|
Process.sleep(15)
|
||||||
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:reblog, visibility, group, users, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("simple_thread", visibility, group, user, friends, non_friends, _opts)
|
defp insert_activity(:simple_thread, "direct", group, users, _opts) do
|
||||||
when visibility in ["public", "unlisted", "private"] do
|
actor = get_actor(group, users)
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
|
||||||
tasks = get_reply_tasks(visibility, group)
|
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "Simple status", visibility: visibility})
|
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
|
||||||
insert_replies(tasks, visibility, user, friends, non_friends, acc)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_activity("simple_thread", "direct", group, user, friends, non_friends, _opts) do
|
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
|
||||||
tasks = get_reply_tasks("direct", group)
|
tasks = get_reply_tasks("direct", group)
|
||||||
|
|
||||||
list =
|
list =
|
||||||
case group do
|
case group do
|
||||||
"non_friends" ->
|
:user ->
|
||||||
Enum.take(non_friends, 3)
|
group = Enum.random(@friends_groups)
|
||||||
|
Enum.take(users[group], 3)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Enum.take(friends, 3)
|
Enum.take(users[group], 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
data = Enum.map(list, &("@" <> &1.nickname))
|
data = Enum.map(list, &("@" <> &1.nickname))
|
||||||
|
@ -339,40 +364,30 @@ defp insert_activity("simple_thread", "direct", group, user, friends, non_friend
|
||||||
visibility: "direct"
|
visibility: "direct"
|
||||||
})
|
})
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]}
|
acc = {activity.id, ["@" <> users[:user].nickname | data] ++ ["reply to status"]}
|
||||||
insert_direct_replies(tasks, user, list, acc)
|
insert_direct_replies(tasks, users[:user], list, acc)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("remote", _, "user", _, _, _, _), do: :ok
|
defp insert_activity(:simple_thread, visibility, group, users, _opts) do
|
||||||
|
actor = get_actor(group, users)
|
||||||
|
tasks = get_reply_tasks(visibility, group)
|
||||||
|
|
||||||
defp insert_activity("remote", visibility, group, user, _friends, _non_friends, opts) do
|
{:ok, activity} =
|
||||||
remote_friends =
|
CommonAPI.post(users[:user], %{status: "Simple status", visibility: visibility})
|
||||||
Users.get_users(user, limit: opts[:friends_used], local: :external, friends?: true)
|
|
||||||
|
|
||||||
remote_non_friends =
|
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
||||||
Users.get_users(user, limit: opts[:non_friends_used], local: :external, friends?: false)
|
insert_replies(tasks, visibility, users, acc)
|
||||||
|
|
||||||
actor = get_actor(group, user, remote_friends, remote_non_friends)
|
|
||||||
|
|
||||||
{act_data, obj_data} = prepare_activity_data(actor, visibility, user)
|
|
||||||
{activity_data, object_data} = other_data(actor)
|
|
||||||
|
|
||||||
activity_data
|
|
||||||
|> Map.merge(act_data)
|
|
||||||
|> Map.put("object", Map.merge(object_data, obj_data))
|
|
||||||
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_actor("user", user, _friends, _non_friends), do: user
|
defp get_actor(:user, %{user: user}), do: user
|
||||||
defp get_actor("friends", _user, friends, _non_friends), do: Enum.random(friends)
|
defp get_actor(group, users), do: Enum.random(users[group])
|
||||||
defp get_actor("non_friends", _user, _friends, non_friends), do: Enum.random(non_friends)
|
|
||||||
|
|
||||||
defp other_data(actor) do
|
defp other_data(actor, content) do
|
||||||
%{host: host} = URI.parse(actor.ap_id)
|
%{host: host} = URI.parse(actor.ap_id)
|
||||||
datetime = DateTime.utc_now()
|
datetime = DateTime.utc_now()
|
||||||
context_id = "http://#{host}:4000/contexts/#{UUID.generate()}"
|
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||||
activity_id = "http://#{host}:4000/activities/#{UUID.generate()}"
|
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||||
object_id = "http://#{host}:4000/objects/#{UUID.generate()}"
|
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||||
|
|
||||||
activity_data = %{
|
activity_data = %{
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
|
@ -389,7 +404,7 @@ defp other_data(actor) do
|
||||||
"attributedTo" => actor.ap_id,
|
"attributedTo" => actor.ap_id,
|
||||||
"bcc" => [],
|
"bcc" => [],
|
||||||
"bto" => [],
|
"bto" => [],
|
||||||
"content" => "Remote post",
|
"content" => content,
|
||||||
"context" => context_id,
|
"context" => context_id,
|
||||||
"conversation" => context_id,
|
"conversation" => context_id,
|
||||||
"emoji" => %{},
|
"emoji" => %{},
|
||||||
|
@ -475,51 +490,65 @@ defp prepare_activity_data(_actor, "direct", mention) do
|
||||||
{act_data, obj_data}
|
{act_data, obj_data}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_reply_tasks("public", "user"), do: ~w(friend non_friend user)
|
defp get_reply_tasks("public", :user) do
|
||||||
defp get_reply_tasks("public", "friends"), do: ~w(non_friend user friend)
|
[:friends_local, :friends_remote, :non_friends_local, :non_friends_remote, :user]
|
||||||
defp get_reply_tasks("public", "non_friends"), do: ~w(user friend non_friend)
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "user") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks("public", group) when group in @friends_groups do
|
||||||
do: ~w(friend user friend)
|
[:non_friends_local, :non_friends_remote, :user, :friends_local, :friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "friends") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks("public", group) when group in @non_friends_groups do
|
||||||
do: ~w(user friend user)
|
[:user, :friends_local, :friends_remote, :non_friends_local, :non_friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "non_friends") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks(visibility, :user) when visibility in ["unlisted", "private"] do
|
||||||
|
[:friends_local, :friends_remote, :user, :friends_local, :friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_reply_tasks(visibility, group)
|
||||||
|
when visibility in ["unlisted", "private"] and group in @friends_groups do
|
||||||
|
[:user, :friends_remote, :friends_local, :user]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_reply_tasks(visibility, group)
|
||||||
|
when visibility in ["unlisted", "private"] and
|
||||||
|
group in @non_friends_groups,
|
||||||
do: []
|
do: []
|
||||||
|
|
||||||
defp get_reply_tasks("direct", "user"), do: ~w(friend user friend)
|
defp get_reply_tasks("direct", :user), do: [:friends_local, :user, :friends_remote]
|
||||||
defp get_reply_tasks("direct", "friends"), do: ~w(user friend user)
|
|
||||||
defp get_reply_tasks("direct", "non_friends"), do: ~w(user non_friend user)
|
|
||||||
|
|
||||||
defp insert_replies(tasks, visibility, user, friends, non_friends, acc) do
|
defp get_reply_tasks("direct", group) when group in @friends_groups,
|
||||||
|
do: [:user, group, :user]
|
||||||
|
|
||||||
|
defp get_reply_tasks("direct", group) when group in @non_friends_groups do
|
||||||
|
[:user, :non_friends_remote, :user, :non_friends_local]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_replies(tasks, visibility, users, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
"friend", {id, data} ->
|
:user, {id, data} ->
|
||||||
friend = Enum.random(friends)
|
insert_reply(users[:user], data, id, visibility)
|
||||||
insert_reply(friend, data, id, visibility)
|
|
||||||
|
|
||||||
"non_friend", {id, data} ->
|
group, {id, data} ->
|
||||||
non_friend = Enum.random(non_friends)
|
replier = Enum.random(users[group])
|
||||||
insert_reply(non_friend, data, id, visibility)
|
insert_reply(replier, data, id, visibility)
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
insert_reply(user, data, id, visibility)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_direct_replies(tasks, user, list, acc) do
|
defp insert_direct_replies(tasks, user, list, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
group, {id, data} when group in ["friend", "non_friend"] ->
|
:user, {id, data} ->
|
||||||
|
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
|
||||||
|
{reply_id, data}
|
||||||
|
|
||||||
|
_, {id, data} ->
|
||||||
actor = Enum.random(list)
|
actor = Enum.random(list)
|
||||||
|
|
||||||
{reply_id, _} =
|
{reply_id, _} =
|
||||||
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
|
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
|
||||||
|
|
||||||
{reply_id, data}
|
{reply_id, data}
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
|
|
||||||
{reply_id, data}
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ defp fetch_timelines(user) do
|
||||||
fetch_home_timeline(user)
|
fetch_home_timeline(user)
|
||||||
fetch_direct_timeline(user)
|
fetch_direct_timeline(user)
|
||||||
fetch_public_timeline(user)
|
fetch_public_timeline(user)
|
||||||
|
fetch_public_timeline(user, :with_blocks)
|
||||||
fetch_public_timeline(user, :local)
|
fetch_public_timeline(user, :local)
|
||||||
fetch_public_timeline(user, :tag)
|
fetch_public_timeline(user, :tag)
|
||||||
fetch_notifications(user)
|
fetch_notifications(user)
|
||||||
|
@ -227,6 +228,58 @@ defp fetch_public_timeline(user, :only_media) do
|
||||||
fetch_public_timeline(opts, "public timeline only media")
|
fetch_public_timeline(opts, "public timeline only media")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_public_timeline(user, :with_blocks) do
|
||||||
|
opts = opts_for_public_timeline(user)
|
||||||
|
|
||||||
|
remote_non_friends = Agent.get(:non_friends_remote, & &1)
|
||||||
|
|
||||||
|
Benchee.run(%{
|
||||||
|
"public timeline without blocks" => fn ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
Enum.each(remote_non_friends, fn non_friend ->
|
||||||
|
{:ok, _} = User.block(user, non_friend)
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
opts = Map.put(opts, "blocking_user", user)
|
||||||
|
|
||||||
|
Benchee.run(
|
||||||
|
%{
|
||||||
|
"public timeline with user block" => fn ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
domains =
|
||||||
|
Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
|
||||||
|
{:ok, _user} = User.unblock(user, non_friend)
|
||||||
|
%{host: host} = URI.parse(non_friend.ap_id)
|
||||||
|
[host | domains]
|
||||||
|
end)
|
||||||
|
|
||||||
|
domains = Enum.uniq(domains)
|
||||||
|
|
||||||
|
Enum.each(domains, fn domain ->
|
||||||
|
{:ok, _} = User.block_domain(user, domain)
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
opts = Map.put(opts, "blocking_user", user)
|
||||||
|
|
||||||
|
Benchee.run(
|
||||||
|
%{
|
||||||
|
"public timeline with domain block" => fn opts ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp fetch_public_timeline(opts, title) when is_binary(title) do
|
defp fetch_public_timeline(opts, title) when is_binary(title) do
|
||||||
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
|
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ def generate(opts \\ []) do
|
||||||
|
|
||||||
make_friends(main_user, opts[:friends])
|
make_friends(main_user, opts[:friends])
|
||||||
|
|
||||||
Repo.get(User, main_user.id)
|
User.get_by_id(main_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_users(max) do
|
def generate_users(max) do
|
||||||
|
@ -166,4 +166,24 @@ defp run_stream(users, main_user) do
|
||||||
)
|
)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec prepare_users(User.t(), keyword()) :: map()
|
||||||
|
def prepare_users(user, opts) do
|
||||||
|
friends_limit = opts[:friends_used]
|
||||||
|
non_friends_limit = opts[:non_friends_used]
|
||||||
|
|
||||||
|
%{
|
||||||
|
user: user,
|
||||||
|
friends_local: fetch_users(user, friends_limit, :local, true),
|
||||||
|
friends_remote: fetch_users(user, friends_limit, :external, true),
|
||||||
|
non_friends_local: fetch_users(user, non_friends_limit, :local, false),
|
||||||
|
non_friends_remote: fetch_users(user, non_friends_limit, :external, false)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_users(user, limit, local, friends?) do
|
||||||
|
user
|
||||||
|
|> get_users(limit: limit, local: local, friends?: friends?)
|
||||||
|
|> Enum.shuffle()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.MastodonAPI.TimelineController
|
|
||||||
|
|
||||||
def run(_args) do
|
def run(_args) do
|
||||||
Mix.Pleroma.start_pleroma()
|
Mix.Pleroma.start_pleroma()
|
||||||
|
@ -37,7 +36,7 @@ def run(_args) do
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Hashtag fetching, any" => fn tags ->
|
"Hashtag fetching, any" => fn tags ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"any" => tags
|
"any" => tags
|
||||||
},
|
},
|
||||||
|
@ -47,7 +46,7 @@ def run(_args) do
|
||||||
end,
|
end,
|
||||||
# Will always return zero results because no overlapping hashtags are generated.
|
# Will always return zero results because no overlapping hashtags are generated.
|
||||||
"Hashtag fetching, all" => fn tags ->
|
"Hashtag fetching, all" => fn tags ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"all" => tags
|
"all" => tags
|
||||||
},
|
},
|
||||||
|
@ -67,7 +66,7 @@ def run(_args) do
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Hashtag fetching" => fn tag ->
|
"Hashtag fetching" => fn tag ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"tag" => tag
|
"tag" => tag
|
||||||
},
|
},
|
||||||
|
@ -80,4 +79,35 @@ def run(_args) do
|
||||||
time: 5
|
time: 5
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp hashtag_fetching(params, user, local_only) do
|
||||||
|
tags =
|
||||||
|
[params["tag"], params["any"]]
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
tag_all =
|
||||||
|
params
|
||||||
|
|> Map.get("all", [])
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
tag_reject =
|
||||||
|
params
|
||||||
|
|> Map.get("none", [])
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
_activities =
|
||||||
|
params
|
||||||
|
|> Map.put("type", "Create")
|
||||||
|
|> Map.put("local_only", local_only)
|
||||||
|
|> Map.put("blocking_user", user)
|
||||||
|
|> Map.put("muting_user", user)
|
||||||
|
|> Map.put("user", user)
|
||||||
|
|> Map.put("tag", tags)
|
||||||
|
|> Map.put("tag_all", tag_all)
|
||||||
|
|> Map.put("tag_reject", tag_reject)
|
||||||
|
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,7 +171,8 @@
|
||||||
"application/ld+json" => ["activity+json"]
|
"application/ld+json" => ["activity+json"]
|
||||||
}
|
}
|
||||||
|
|
||||||
config :tesla, adapter: Tesla.Adapter.Gun
|
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||||
|
|
||||||
# Configures http settings, upstream proxy etc.
|
# Configures http settings, upstream proxy etc.
|
||||||
config :pleroma, :http,
|
config :pleroma, :http,
|
||||||
proxy_url: nil,
|
proxy_url: nil,
|
||||||
|
@ -183,7 +184,7 @@
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
email: "example@example.com",
|
email: "example@example.com",
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "Pleroma: An efficient and flexible fediverse server",
|
||||||
background_image: "/images/city.jpg",
|
background_image: "/images/city.jpg",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
chat_limit: 5_000,
|
chat_limit: 5_000,
|
||||||
|
|
|
@ -547,7 +547,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"totalReports" : 1,
|
"total" : 1,
|
||||||
"reports": [
|
"reports": [
|
||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
@ -768,7 +768,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
||||||
- On success: `204`, empty response
|
- On success: `204`, empty response
|
||||||
|
|
||||||
## `POST /api/pleroma/admin/reports/:report_id/notes/:id`
|
## `DELETE /api/pleroma/admin/reports/:report_id/notes/:id`
|
||||||
|
|
||||||
### Delete report note
|
### Delete report note
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,6 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
|
||||||
|
|
||||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
||||||
|
|
||||||
## Attachment cap
|
|
||||||
|
|
||||||
Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
|
|
||||||
|
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
@ -32,12 +28,20 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `thread_muted`: true if the thread the post belongs to is muted
|
- `thread_muted`: true if the thread the post belongs to is muted
|
||||||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
||||||
|
|
||||||
## Attachments
|
## Media Attachments
|
||||||
|
|
||||||
Has these additional fields under the `pleroma` object:
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `mime_type`: mime type of the attachment.
|
- `mime_type`: mime type of the attachment.
|
||||||
|
|
||||||
|
### Attachment cap
|
||||||
|
|
||||||
|
Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
Pleroma does not process remote images and therefore cannot include fields such as `meta` and `blurhash`. It does not support focal points or aspect ratios. The frontend is expected to handle it.
|
||||||
|
|
||||||
## Accounts
|
## Accounts
|
||||||
|
|
||||||
The `id` parameter can also be the `nickname` of the user. This only works in these endpoints, not the deeper nested ones for following etc.
|
The `id` parameter can also be the `nickname` of the user. This only works in these endpoints, not the deeper nested ones for following etc.
|
||||||
|
|
|
@ -69,3 +69,32 @@ mix pleroma.database update_users_following_followers_counts
|
||||||
```sh tab="From Source"
|
```sh tab="From Source"
|
||||||
mix pleroma.database fix_likes_collections
|
mix pleroma.database fix_likes_collections
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Vacuum the database
|
||||||
|
|
||||||
|
### Analyze
|
||||||
|
|
||||||
|
Running an `analyze` vacuum job can improve performance by updating statistics used by the query planner. **It is safe to cancel this.**
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl database vacuum analyze
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.database vacuum analyze
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full
|
||||||
|
|
||||||
|
Running a `full` vacuum job rebuilds your entire database by reading all of the data and rewriting it into smaller
|
||||||
|
and more compact files with an optimized layout. This process will take a long time and use additional disk space as
|
||||||
|
it builds the files side-by-side the existing database files. It can make your database faster and use less disk space,
|
||||||
|
but should only be run if necessary. **It is safe to cancel this.**
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl database vacuum full
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.database vacuum full
|
||||||
|
```
|
|
@ -42,6 +42,12 @@ Feel free to contact us to be added to this list!
|
||||||
- Platforms: SailfishOS
|
- Platforms: SailfishOS
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Husky
|
||||||
|
- Source code: <https://git.mentality.rip/FWGS/Husky>
|
||||||
|
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||||
|
|
||||||
### Nekonium
|
### Nekonium
|
||||||
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
||||||
- Source: <https://gogs.gdgd.jp.net/lin/nekonium>
|
- Source: <https://gogs.gdgd.jp.net/lin/nekonium>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Database do
|
defmodule Mix.Tasks.Pleroma.Database do
|
||||||
alias Pleroma.Conversation
|
alias Pleroma.Conversation
|
||||||
|
alias Pleroma.Maintenance
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -34,13 +35,7 @@ def run(["remove_embedded_objects" | args]) do
|
||||||
)
|
)
|
||||||
|
|
||||||
if Keyword.get(options, :vacuum) do
|
if Keyword.get(options, :vacuum) do
|
||||||
Logger.info("Runnning VACUUM FULL")
|
Maintenance.vacuum("full")
|
||||||
|
|
||||||
Repo.query!(
|
|
||||||
"vacuum full;",
|
|
||||||
[],
|
|
||||||
timeout: :infinity
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -94,13 +89,7 @@ def run(["prune_objects" | args]) do
|
||||||
|> Repo.delete_all(timeout: :infinity)
|
|> Repo.delete_all(timeout: :infinity)
|
||||||
|
|
||||||
if Keyword.get(options, :vacuum) do
|
if Keyword.get(options, :vacuum) do
|
||||||
Logger.info("Runnning VACUUM FULL")
|
Maintenance.vacuum("full")
|
||||||
|
|
||||||
Repo.query!(
|
|
||||||
"vacuum full;",
|
|
||||||
[],
|
|
||||||
timeout: :infinity
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,4 +124,10 @@ def run(["fix_likes_collections"]) do
|
||||||
end)
|
end)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def run(["vacuum", args]) do
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
Maintenance.vacuum(args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,6 @@ defmodule Pleroma.Constants do
|
||||||
|
|
||||||
const(static_only_files,
|
const(static_only_files,
|
||||||
do:
|
do:
|
||||||
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
|
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,22 +22,7 @@ def options(connection_opts \\ [], %URI{} = uri) do
|
||||||
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_scheme_opts(opts, %URI{scheme: "http"}), do: opts
|
defp add_scheme_opts(opts, _), do: opts
|
||||||
|
|
||||||
defp add_scheme_opts(opts, %URI{scheme: "https", host: host}) do
|
|
||||||
ssl_opts = [
|
|
||||||
ssl_options: [
|
|
||||||
# Workaround for remote server certificate chain issues
|
|
||||||
partial_chain: &:hackney_connect.partial_chain/1,
|
|
||||||
|
|
||||||
# We don't support TLS v1.3 yet
|
|
||||||
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
|
|
||||||
server_name_indication: to_charlist(host)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
Keyword.merge(opts, ssl_opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_request(_), do: :ok
|
def after_request(_), do: :ok
|
||||||
end
|
end
|
||||||
|
|
37
lib/pleroma/maintenance.ex
Normal file
37
lib/pleroma/maintenance.ex
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Maintenance do
|
||||||
|
alias Pleroma.Repo
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def vacuum(args) do
|
||||||
|
case args do
|
||||||
|
"analyze" ->
|
||||||
|
Logger.info("Runnning VACUUM ANALYZE.")
|
||||||
|
|
||||||
|
Repo.query!(
|
||||||
|
"vacuum analyze;",
|
||||||
|
[],
|
||||||
|
timeout: :infinity
|
||||||
|
)
|
||||||
|
|
||||||
|
"full" ->
|
||||||
|
Logger.info("Runnning VACUUM FULL.")
|
||||||
|
|
||||||
|
Logger.warn(
|
||||||
|
"Re-packing your entire database may take a while and will consume extra disk space during the process."
|
||||||
|
)
|
||||||
|
|
||||||
|
Repo.query!(
|
||||||
|
"vacuum full;",
|
||||||
|
[],
|
||||||
|
timeout: :infinity
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Logger.error("Error: invalid vacuum argument.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -31,7 +31,7 @@ defp headers do
|
||||||
{"x-content-type-options", "nosniff"},
|
{"x-content-type-options", "nosniff"},
|
||||||
{"referrer-policy", referrer_policy},
|
{"referrer-policy", referrer_policy},
|
||||||
{"x-download-options", "noopen"},
|
{"x-download-options", "noopen"},
|
||||||
{"content-security-policy", csp_string() <> ";"}
|
{"content-security-policy", csp_string()}
|
||||||
]
|
]
|
||||||
|
|
||||||
if report_uri do
|
if report_uri do
|
||||||
|
@ -43,23 +43,46 @@ defp headers do
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
headers ++ [{"reply-to", Jason.encode!(report_group)}]
|
[{"reply-to", Jason.encode!(report_group)} | headers]
|
||||||
else
|
else
|
||||||
headers
|
headers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
static_csp_rules = [
|
||||||
|
"default-src 'none'",
|
||||||
|
"base-uri 'self'",
|
||||||
|
"frame-ancestors 'none'",
|
||||||
|
"style-src 'self' 'unsafe-inline'",
|
||||||
|
"font-src 'self'",
|
||||||
|
"manifest-src 'self'"
|
||||||
|
]
|
||||||
|
|
||||||
|
@csp_start [Enum.join(static_csp_rules, ";") <> ";"]
|
||||||
|
|
||||||
defp csp_string do
|
defp csp_string do
|
||||||
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
||||||
static_url = Pleroma.Web.Endpoint.static_url()
|
static_url = Pleroma.Web.Endpoint.static_url()
|
||||||
websocket_url = Pleroma.Web.Endpoint.websocket_url()
|
websocket_url = Pleroma.Web.Endpoint.websocket_url()
|
||||||
report_uri = Config.get([:http_security, :report_uri])
|
report_uri = Config.get([:http_security, :report_uri])
|
||||||
|
|
||||||
connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
|
img_src = "img-src 'self' data: blob:"
|
||||||
|
media_src = "media-src 'self'"
|
||||||
|
|
||||||
|
{img_src, media_src} =
|
||||||
|
if Config.get([:media_proxy, :enabled]) &&
|
||||||
|
!Config.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do
|
||||||
|
sources = get_proxy_and_attachment_sources()
|
||||||
|
{[img_src, sources], [media_src, sources]}
|
||||||
|
else
|
||||||
|
{[img_src, " https:"], [media_src, " https:"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
connect_src = ["connect-src 'self' blob: ", static_url, ?\s, websocket_url]
|
||||||
|
|
||||||
connect_src =
|
connect_src =
|
||||||
if Pleroma.Config.get(:env) == :dev do
|
if Pleroma.Config.get(:env) == :dev do
|
||||||
connect_src <> " http://localhost:3035/"
|
[connect_src, " http://localhost:3035/"]
|
||||||
else
|
else
|
||||||
connect_src
|
connect_src
|
||||||
end
|
end
|
||||||
|
@ -71,27 +94,46 @@ defp csp_string do
|
||||||
"script-src 'self'"
|
"script-src 'self'"
|
||||||
end
|
end
|
||||||
|
|
||||||
main_part = [
|
report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
|
||||||
"default-src 'none'",
|
insecure = if scheme == "https", do: "upgrade-insecure-requests"
|
||||||
"base-uri 'self'",
|
|
||||||
"frame-ancestors 'none'",
|
|
||||||
"img-src 'self' data: blob: https:",
|
|
||||||
"media-src 'self' https:",
|
|
||||||
"style-src 'self' 'unsafe-inline'",
|
|
||||||
"font-src 'self'",
|
|
||||||
"manifest-src 'self'",
|
|
||||||
connect_src,
|
|
||||||
script_src
|
|
||||||
]
|
|
||||||
|
|
||||||
report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: []
|
@csp_start
|
||||||
|
|> add_csp_param(img_src)
|
||||||
insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: []
|
|> add_csp_param(media_src)
|
||||||
|
|> add_csp_param(connect_src)
|
||||||
(main_part ++ report ++ insecure)
|
|> add_csp_param(script_src)
|
||||||
|> Enum.join("; ")
|
|> add_csp_param(insecure)
|
||||||
|
|> add_csp_param(report)
|
||||||
|
|> :erlang.iolist_to_binary()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_proxy_and_attachment_sources do
|
||||||
|
media_proxy_whitelist =
|
||||||
|
Enum.reduce(Config.get([:media_proxy, :whitelist]), [], fn host, acc ->
|
||||||
|
add_source(acc, host)
|
||||||
|
end)
|
||||||
|
|
||||||
|
upload_base_url =
|
||||||
|
if Config.get([Pleroma.Upload, :base_url]),
|
||||||
|
do: URI.parse(Config.get([Pleroma.Upload, :base_url])).host
|
||||||
|
|
||||||
|
s3_endpoint =
|
||||||
|
if Config.get([Pleroma.Upload, :uploader]) == Pleroma.Uploaders.S3,
|
||||||
|
do: URI.parse(Config.get([Pleroma.Uploaders.S3, :public_endpoint])).host
|
||||||
|
|
||||||
|
[]
|
||||||
|
|> add_source(upload_base_url)
|
||||||
|
|> add_source(s3_endpoint)
|
||||||
|
|> add_source(media_proxy_whitelist)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_source(iodata, nil), do: iodata
|
||||||
|
defp add_source(iodata, source), do: [[?\s, source] | iodata]
|
||||||
|
|
||||||
|
defp add_csp_param(csp_iodata, nil), do: csp_iodata
|
||||||
|
|
||||||
|
defp add_csp_param(csp_iodata, param), do: [[param, ?;] | csp_iodata]
|
||||||
|
|
||||||
def warn_if_disabled do
|
def warn_if_disabled do
|
||||||
unless Config.get([:http_security, :enabled]) do
|
unless Config.get([:http_security, :enabled]) do
|
||||||
Logger.warn("
|
Logger.warn("
|
||||||
|
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.User.Query do
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
exclude_service_users: boolean(),
|
invisible: boolean(),
|
||||||
followers: User.t(),
|
followers: User.t(),
|
||||||
friends: User.t(),
|
friends: User.t(),
|
||||||
recipients_from_activity: [String.t()],
|
recipients_from_activity: [String.t()],
|
||||||
|
@ -89,8 +89,8 @@ defp compose_query({key, value}, query)
|
||||||
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:exclude_service_users, _}, query) do
|
defp compose_query({:invisible, bool}, query) when is_boolean(bool) do
|
||||||
where(query, [u], not like(u.ap_id, "%/relay") and not like(u.ap_id, "%/internal/fetch"))
|
where(query, [u], u.invisible == ^bool)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({key, value}, query)
|
defp compose_query({key, value}, query)
|
||||||
|
|
|
@ -936,6 +936,12 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||||
[activity, object: o] in query,
|
[activity, object: o] in query,
|
||||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
|
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
|
||||||
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
|
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"recipients_contain_blocked_domains(?, ?) = false",
|
||||||
|
activity.recipients,
|
||||||
|
^domain_blocks
|
||||||
|
),
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
|
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
|
||||||
|
@ -1030,6 +1036,17 @@ defp exclude_poll_votes(query, _) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp exclude_invisible_actors(query, %{"invisible_actors" => true}), do: query
|
||||||
|
|
||||||
|
defp exclude_invisible_actors(query, _opts) do
|
||||||
|
invisible_ap_ids =
|
||||||
|
User.Query.build(%{invisible: true, select: [:ap_id]})
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
|
||||||
|
|
||||||
|
from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
|
||||||
|
end
|
||||||
|
|
||||||
defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
|
defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
|
||||||
from(activity in query, where: activity.id != ^id)
|
from(activity in query, where: activity.id != ^id)
|
||||||
end
|
end
|
||||||
|
@ -1135,6 +1152,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_instance(opts)
|
|> restrict_instance(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|
|> exclude_invisible_actors(opts)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
@ -85,15 +86,20 @@ def like(actor, object) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||||
def announce(actor, object, options \\ []) do
|
def announce(actor, object, options \\ []) do
|
||||||
public? = Keyword.get(options, :public, false)
|
public? = Keyword.get(options, :public, false)
|
||||||
to = [actor.follower_address, object.data["actor"]]
|
|
||||||
|
|
||||||
to =
|
to =
|
||||||
if public? do
|
cond do
|
||||||
[Pleroma.Constants.as_public() | to]
|
actor.ap_id == Relay.relay_ap_id() ->
|
||||||
else
|
[actor.follower_address]
|
||||||
to
|
|
||||||
|
public? ->
|
||||||
|
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
[actor.follower_address, object.data["actor"]]
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
|
@ -33,11 +33,14 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
||||||
# - Stream out the announce
|
# - Stream out the announce
|
||||||
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
||||||
announced_object = Object.get_by_ap_id(object.data["object"])
|
announced_object = Object.get_by_ap_id(object.data["object"])
|
||||||
|
user = User.get_cached_by_ap_id(object.data["actor"])
|
||||||
|
|
||||||
Utils.add_announce_to_object(object, announced_object)
|
Utils.add_announce_to_object(object, announced_object)
|
||||||
|
|
||||||
|
if !User.is_internal_user?(user) do
|
||||||
Notification.create_notifications(object)
|
Notification.create_notifications(object)
|
||||||
ActivityPub.stream_out(object)
|
ActivityPub.stream_out(object)
|
||||||
|
end
|
||||||
|
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
|
|
|
@ -740,6 +740,7 @@ defp build_flag_object(_), do: []
|
||||||
def get_reports(params, page, page_size) do
|
def get_reports(params, page, page_size) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||||
|> Map.put("type", "Flag")
|
|> Map.put("type", "Flag")
|
||||||
|> Map.put("skip_preload", true)
|
|> Map.put("skip_preload", true)
|
||||||
|> Map.put("preload_report_notes", true)
|
|> Map.put("preload_report_notes", true)
|
||||||
|
|
|
@ -7,31 +7,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.ReportNote
|
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.AdminAPI
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
alias Pleroma.Web.AdminAPI.Report
|
|
||||||
alias Pleroma.Web.AdminAPI.ReportView
|
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MastodonAPI
|
|
||||||
alias Pleroma.Web.MastodonAPI.AppView
|
|
||||||
alias Pleroma.Web.OAuth.App
|
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -66,32 +56,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:invites"], admin: true}
|
|
||||||
when action in [:create_invite_token, :revoke_invite, :email_invite]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:follows"], admin: true}
|
%{scopes: ["write:follows"], admin: true}
|
||||||
when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
|
when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["read:reports"], admin: true}
|
|
||||||
when action in [:list_reports, :report_show]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:reports"], admin: true}
|
|
||||||
when action in [:reports_update, :report_notes_create, :report_notes_delete]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"], admin: true}
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
|
@ -116,10 +86,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
:restart,
|
:restart,
|
||||||
:resend_confirmation_email,
|
:resend_confirmation_email,
|
||||||
:confirm_email,
|
:confirm_email,
|
||||||
:oauth_app_create,
|
|
||||||
:oauth_app_list,
|
|
||||||
:oauth_app_update,
|
|
||||||
:oauth_app_delete,
|
|
||||||
:reload_emoji
|
:reload_emoji
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -288,7 +254,7 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|> put_view(AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
else
|
else
|
||||||
_ -> {:error, :not_found}
|
_ -> {:error, :not_found}
|
||||||
|
@ -569,69 +535,6 @@ def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Sends registration invite via email"
|
|
||||||
def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
|
|
||||||
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
|
|
||||||
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
|
||||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
|
||||||
email <-
|
|
||||||
Pleroma.Emails.UserEmail.user_invitation_email(
|
|
||||||
user,
|
|
||||||
invite_token,
|
|
||||||
email,
|
|
||||||
params["name"]
|
|
||||||
),
|
|
||||||
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
{:registrations_open, _} ->
|
|
||||||
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
|
||||||
|
|
||||||
{:invites_enabled, _} ->
|
|
||||||
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Create an account registration invite token"
|
|
||||||
def create_invite_token(conn, params) do
|
|
||||||
opts = %{}
|
|
||||||
|
|
||||||
opts =
|
|
||||||
if params["max_use"],
|
|
||||||
do: Map.put(opts, :max_use, params["max_use"]),
|
|
||||||
else: opts
|
|
||||||
|
|
||||||
opts =
|
|
||||||
if params["expires_at"],
|
|
||||||
do: Map.put(opts, :expires_at, params["expires_at"]),
|
|
||||||
else: opts
|
|
||||||
|
|
||||||
{:ok, invite} = UserInviteToken.create_invite(opts)
|
|
||||||
|
|
||||||
json(conn, AccountView.render("invite.json", %{invite: invite}))
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Get list of created invites"
|
|
||||||
def invites(conn, _params) do
|
|
||||||
invites = UserInviteToken.list_invites()
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("invites.json", %{invites: invites})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Revokes invite by token"
|
|
||||||
def revoke_invite(conn, %{"token" => token}) do
|
|
||||||
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
|
||||||
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("invite.json", %{invite: updated_invite})
|
|
||||||
else
|
|
||||||
nil -> {:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Get a password reset token (base64 string) for given nickname"
|
@doc "Get a password reset token (base64 string) for given nickname"
|
||||||
def get_password_reset(conn, %{"nickname" => nickname}) do
|
def get_password_reset(conn, %{"nickname" => nickname}) do
|
||||||
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
|
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
|
||||||
|
@ -718,85 +621,6 @@ def update_user_credentials(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_reports(conn, params) do
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
reports = Utils.get_reports(params, page, page_size)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ReportView)
|
|
||||||
|> render("index.json", %{reports: reports})
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_show(conn, %{"id" => id}) do
|
|
||||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
|
||||||
conn
|
|
||||||
|> put_view(ReportView)
|
|
||||||
|> render("show.json", Report.extract_report_info(report))
|
|
||||||
else
|
|
||||||
_ -> {:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
|
|
||||||
result =
|
|
||||||
reports
|
|
||||||
|> Enum.map(fn report ->
|
|
||||||
with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_update",
|
|
||||||
actor: admin,
|
|
||||||
subject: activity
|
|
||||||
})
|
|
||||||
|
|
||||||
activity
|
|
||||||
else
|
|
||||||
{:error, message} -> %{id: report["id"], error: message}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
case Enum.any?(result, &Map.has_key?(&1, :error)) do
|
|
||||||
true -> json_response(conn, :bad_request, result)
|
|
||||||
false -> json_response(conn, :no_content, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_notes_create(%{assigns: %{user: user}} = conn, %{
|
|
||||||
"id" => report_id,
|
|
||||||
"content" => content
|
|
||||||
}) do
|
|
||||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_note",
|
|
||||||
actor: user,
|
|
||||||
subject: Activity.get_by_id(report_id),
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
|
||||||
"id" => note_id,
|
|
||||||
"report_id" => report_id
|
|
||||||
}) do
|
|
||||||
with {:ok, note} <- ReportNote.destroy(note_id) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_note_delete",
|
|
||||||
actor: user,
|
|
||||||
subject: Activity.get_by_id(report_id),
|
|
||||||
text: note.content
|
|
||||||
})
|
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_log(conn, params) do
|
def list_log(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
@ -869,83 +693,6 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
conn |> json("")
|
conn |> json("")
|
||||||
end
|
end
|
||||||
|
|
||||||
def oauth_app_create(conn, params) do
|
|
||||||
params =
|
|
||||||
if params["name"] do
|
|
||||||
Map.put(params, "client_name", params["name"])
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
result =
|
|
||||||
case App.create(params) do
|
|
||||||
{:ok, app} ->
|
|
||||||
AppView.render("show.json", %{app: app, admin: true})
|
|
||||||
|
|
||||||
{:error, changeset} ->
|
|
||||||
App.errors(changeset)
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_update(conn, params) do
|
|
||||||
params =
|
|
||||||
if params["name"] do
|
|
||||||
Map.put(params, "client_name", params["name"])
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, app} <- App.update(params) do
|
|
||||||
json(conn, AppView.render("show.json", %{app: app, admin: true}))
|
|
||||||
else
|
|
||||||
{:error, changeset} ->
|
|
||||||
json(conn, App.errors(changeset))
|
|
||||||
|
|
||||||
nil ->
|
|
||||||
json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_list(conn, params) do
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
search_params = %{
|
|
||||||
client_name: params["name"],
|
|
||||||
client_id: params["client_id"],
|
|
||||||
page: page,
|
|
||||||
page_size: page_size
|
|
||||||
}
|
|
||||||
|
|
||||||
search_params =
|
|
||||||
if Map.has_key?(params, "trusted") do
|
|
||||||
Map.put(search_params, :trusted, params["trusted"])
|
|
||||||
else
|
|
||||||
search_params
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, apps, count} <- App.search(search_params) do
|
|
||||||
json(
|
|
||||||
conn,
|
|
||||||
AppView.render("index.json",
|
|
||||||
apps: apps,
|
|
||||||
count: count,
|
|
||||||
page_size: page_size,
|
|
||||||
admin: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_delete(conn, params) do
|
|
||||||
with {:ok, _app} <- App.destroy(params["id"]) do
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def stats(conn, _) do
|
def stats(conn, _) do
|
||||||
count = Stats.get_status_visibility_count()
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
|
|
78
lib/pleroma/web/admin_api/controllers/invite_controller.ex
Normal file
78
lib/pleroma/web/admin_api/controllers/invite_controller.ex
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# 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.InviteController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:invites"], admin: true} when action in [:create, :revoke, :email]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InviteOperation
|
||||||
|
|
||||||
|
@doc "Get list of created invites"
|
||||||
|
def index(conn, _params) do
|
||||||
|
invites = UserInviteToken.list_invites()
|
||||||
|
|
||||||
|
render(conn, "index.json", invites: invites)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Create an account registration invite token"
|
||||||
|
def create(%{body_params: params} = conn, _) do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(params)
|
||||||
|
|
||||||
|
render(conn, "show.json", invite: invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Revokes invite by token"
|
||||||
|
def revoke(%{body_params: %{token: token}} = conn, _) do
|
||||||
|
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
|
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
||||||
|
render(conn, "show.json", invite: updated_invite)
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Sends registration invite via email"
|
||||||
|
def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do
|
||||||
|
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
|
||||||
|
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
||||||
|
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||||
|
{:ok, _} <-
|
||||||
|
user
|
||||||
|
|> Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
invite_token,
|
||||||
|
email,
|
||||||
|
params[:name]
|
||||||
|
)
|
||||||
|
|> Pleroma.Emails.Mailer.deliver() do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
{:registrations_open, _} ->
|
||||||
|
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
||||||
|
|
||||||
|
{:invites_enabled, _} ->
|
||||||
|
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,87 @@
|
||||||
|
# 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.OAuthAppController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(:put_view, Pleroma.Web.MastodonAPI.AppView)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write"], admin: true}
|
||||||
|
when action in [:create, :index, :update, :delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.OAuthAppOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
search_params =
|
||||||
|
params
|
||||||
|
|> Map.take([:client_id, :page, :page_size, :trusted])
|
||||||
|
|> Map.put(:client_name, params[:name])
|
||||||
|
|
||||||
|
with {:ok, apps, count} <- App.search(search_params) do
|
||||||
|
render(conn, "index.json",
|
||||||
|
apps: apps,
|
||||||
|
count: count,
|
||||||
|
page_size: params.page_size,
|
||||||
|
admin: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(%{body_params: params} = conn, _) do
|
||||||
|
params =
|
||||||
|
if params[:name] do
|
||||||
|
Map.put(params, :client_name, params[:name])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
case App.create(params) do
|
||||||
|
{:ok, app} ->
|
||||||
|
render(conn, "show.json", app: app, admin: true)
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{body_params: params} = conn, %{id: id}) do
|
||||||
|
params =
|
||||||
|
if params[:name] do
|
||||||
|
Map.put(params, :client_name, params.name)
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, app} <- App.update(id, params) do
|
||||||
|
render(conn, "show.json", app: app, admin: true)
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(conn, params) do
|
||||||
|
with {:ok, _app} <- App.destroy(params.id) do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
107
lib/pleroma/web/admin_api/controllers/report_controller.ex
Normal file
107
lib/pleroma/web/admin_api/controllers/report_controller.ex
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# 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.ReportController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.ReportNote
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} when action in [:index, :show])
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:reports"], admin: true}
|
||||||
|
when action in [:update, :notes_create, :notes_delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
reports = Utils.get_reports(params, params.page, params.page_size)
|
||||||
|
|
||||||
|
render(conn, "index.json", reports: reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{id: id}) do
|
||||||
|
with %Activity{} = report <- Activity.get_by_id(id) do
|
||||||
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
|
else
|
||||||
|
_ -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do
|
||||||
|
result =
|
||||||
|
Enum.map(reports, fn report ->
|
||||||
|
case CommonAPI.update_report_state(report.id, report.state) do
|
||||||
|
{:ok, activity} ->
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity
|
||||||
|
})
|
||||||
|
|
||||||
|
activity
|
||||||
|
|
||||||
|
{:error, message} ->
|
||||||
|
%{id: report.id, error: message}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if Enum.any?(result, &Map.has_key?(&1, :error)) do
|
||||||
|
json_response(conn, :bad_request, result)
|
||||||
|
else
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
|
||||||
|
id: report_id
|
||||||
|
}) do
|
||||||
|
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_note",
|
||||||
|
actor: user,
|
||||||
|
subject: Activity.get_by_id(report_id),
|
||||||
|
text: content
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||||
|
id: note_id,
|
||||||
|
report_id: report_id
|
||||||
|
}) do
|
||||||
|
with {:ok, note} <- ReportNote.destroy(note_id) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_note_delete",
|
||||||
|
actor: user,
|
||||||
|
subject: Activity.get_by_id(report_id),
|
||||||
|
text: note.content
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -41,9 +41,7 @@ def index(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
|
|
||||||
def show(conn, %{id: id}) do
|
def show(conn, %{id: id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id) do
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
conn
|
render(conn, "show.json", %{activity: activity})
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|
||||||
|> render("show.json", %{activity: activity})
|
|
||||||
else
|
else
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ def user(params \\ %{}) do
|
||||||
query =
|
query =
|
||||||
params
|
params
|
||||||
|> Map.drop([:page, :page_size])
|
|> Map.drop([:page, :page_size])
|
||||||
|> Map.put(:exclude_service_users, true)
|
|> Map.put(:invisible, false)
|
||||||
|> User.Query.build()
|
|> User.Query.build()
|
||||||
|> order_by([u], u.nickname)
|
|> order_by([u], u.nickname)
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ def user(params \\ %{}) do
|
||||||
count = Repo.aggregate(query, :count, :id)
|
count = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
results = Repo.all(paginated_query)
|
results = Repo.all(paginated_query)
|
||||||
|
|
||||||
{:ok, results, count}
|
{:ok, results, count}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -80,24 +80,6 @@ def render("show.json", %{user: user}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("invite.json", %{invite: invite}) do
|
|
||||||
%{
|
|
||||||
"id" => invite.id,
|
|
||||||
"token" => invite.token,
|
|
||||||
"used" => invite.used,
|
|
||||||
"expires_at" => invite.expires_at,
|
|
||||||
"uses" => invite.uses,
|
|
||||||
"max_use" => invite.max_use,
|
|
||||||
"invite_type" => invite.invite_type
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("invites.json", %{invites: invites}) do
|
|
||||||
%{
|
|
||||||
invites: render_many(invites, AccountView, "invite.json", as: :invite)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("created.json", %{user: user}) do
|
def render("created.json", %{user: user}) do
|
||||||
%{
|
%{
|
||||||
type: "success",
|
type: "success",
|
||||||
|
|
25
lib/pleroma/web/admin_api/views/invite_view.ex
Normal file
25
lib/pleroma/web/admin_api/views/invite_view.ex
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# 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.InviteView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{invites: invites}) do
|
||||||
|
%{
|
||||||
|
invites: render_many(invites, __MODULE__, "show.json", as: :invite)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{invite: invite}) do
|
||||||
|
%{
|
||||||
|
"id" => invite.id,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => invite.used,
|
||||||
|
"expires_at" => invite.expires_at,
|
||||||
|
"uses" => invite.uses,
|
||||||
|
"max_use" => invite.max_use,
|
||||||
|
"invite_type" => invite.invite_type
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
148
lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
Normal file
148
lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Get a list of generated invites",
|
||||||
|
operationId: "AdminAPI.InviteController.index",
|
||||||
|
security: [%{"oAuth" => ["read:invites"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Invites", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
invites: %Schema{type: :array, items: invite()}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"invites" => [
|
||||||
|
%{
|
||||||
|
"id" => 123,
|
||||||
|
"token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
|
||||||
|
"used" => true,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"uses" => 0,
|
||||||
|
"max_use" => nil,
|
||||||
|
"invite_type" => "one_time"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Create an account registration invite token",
|
||||||
|
operationId: "AdminAPI.InviteController.create",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
max_use: %Schema{type: :integer},
|
||||||
|
expires_at: %Schema{type: :string, format: :date, example: "2020-04-20"}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Invite", "application/json", invite())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def revoke_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Revoke invite by token",
|
||||||
|
operationId: "AdminAPI.InviteController.revoke",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:token],
|
||||||
|
properties: %{
|
||||||
|
token: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Invite", "application/json", invite()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Sends registration invite via email",
|
||||||
|
operationId: "AdminAPI.InviteController.email",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:email],
|
||||||
|
properties: %{
|
||||||
|
email: %Schema{type: :string, format: :email},
|
||||||
|
name: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp invite do
|
||||||
|
%Schema{
|
||||||
|
title: "Invite",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
token: %Schema{type: :string},
|
||||||
|
used: %Schema{type: :boolean},
|
||||||
|
expires_at: %Schema{type: :string, format: :date, nullable: true},
|
||||||
|
uses: %Schema{type: :integer},
|
||||||
|
max_use: %Schema{type: :integer, nullable: true},
|
||||||
|
invite_type: %Schema{
|
||||||
|
type: :string,
|
||||||
|
enum: ["one_time", "reusable", "date_limited", "reusable_date_limited"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => 123,
|
||||||
|
"token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
|
||||||
|
"used" => true,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"uses" => 0,
|
||||||
|
"max_use" => nil,
|
||||||
|
"invite_type" => "one_time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
215
lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex
Normal file
215
lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
summary: "List OAuth apps",
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
operationId: "AdminAPI.OAuthAppController.index",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:name, :query, %Schema{type: :string}, "App name"),
|
||||||
|
Operation.parameter(:client_id, :query, %Schema{type: :string}, "Client ID"),
|
||||||
|
Operation.parameter(:page, :query, %Schema{type: :integer, default: 1}, "Page"),
|
||||||
|
Operation.parameter(
|
||||||
|
:trusted,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Trusted apps"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number of apps to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("List of apps", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
apps: %Schema{type: :array, items: oauth_app()},
|
||||||
|
count: %Schema{type: :integer},
|
||||||
|
page_size: %Schema{type: :integer}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"apps" => [
|
||||||
|
%{
|
||||||
|
"id" => 1,
|
||||||
|
"name" => "App name",
|
||||||
|
"client_id" => "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret" => "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri" => "https://example.com/oauth-callback",
|
||||||
|
"website" => "https://example.com",
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Create OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.create",
|
||||||
|
requestBody: request_body("Parameters", create_request()),
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("App", "application/json", oauth_app()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Update OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.update",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request()),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("App", "application/json", oauth_app()),
|
||||||
|
400 =>
|
||||||
|
Operation.response("Bad Request", "application/json", %Schema{
|
||||||
|
oneOf: [ApiError, %Schema{type: :string}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Delete OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.delete",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => no_content_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthAppCreateRequest",
|
||||||
|
type: :object,
|
||||||
|
required: [:name, :redirect_uris],
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string, description: "Application Name"},
|
||||||
|
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
|
||||||
|
redirect_uris: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||||
|
},
|
||||||
|
website: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description: "A URL to the homepage of the app"
|
||||||
|
},
|
||||||
|
trusted: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
nullable: true,
|
||||||
|
default: false,
|
||||||
|
description: "Is the app trusted?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"name" => "My App",
|
||||||
|
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"scopes" => ["read", "write"],
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthAppUpdateRequest",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string, description: "Application Name"},
|
||||||
|
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
|
||||||
|
redirect_uris: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||||
|
},
|
||||||
|
website: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description: "A URL to the homepage of the app"
|
||||||
|
},
|
||||||
|
trusted: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
nullable: true,
|
||||||
|
default: false,
|
||||||
|
description: "Is the app trusted?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"name" => "My App",
|
||||||
|
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"scopes" => ["read", "write"],
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp oauth_app do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthApp",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
client_id: %Schema{type: :string},
|
||||||
|
client_secret: %Schema{type: :string},
|
||||||
|
redirect_uri: %Schema{type: :string},
|
||||||
|
website: %Schema{type: :string, nullable: true},
|
||||||
|
trusted: %Schema{type: :boolean}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => 123,
|
||||||
|
"name" => "My App",
|
||||||
|
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||||
|
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||||
|
"redirect_uri" => "https://myapp.com/oauth-callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"trusted" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def id_param do
|
||||||
|
Operation.parameter(:id, :path, :integer, "App ID",
|
||||||
|
example: 1337,
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
237
lib/pleroma/web/api_spec/operations/admin/report_operation.ex
Normal file
237
lib/pleroma/web/api_spec/operations/admin/report_operation.ex
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Get a list of reports",
|
||||||
|
operationId: "AdminAPI.ReportController.index",
|
||||||
|
security: [%{"oAuth" => ["read:reports"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:state,
|
||||||
|
:query,
|
||||||
|
report_state(),
|
||||||
|
"Filter by report state"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:limit,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer},
|
||||||
|
"The number of records to retrieve"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page number"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number number of log entries per page"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
total: %Schema{type: :integer},
|
||||||
|
reports: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Get an individual report",
|
||||||
|
operationId: "AdminAPI.ReportController.show",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Report", "application/json", report()),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Change the state of one or multiple reports",
|
||||||
|
operationId: "AdminAPI.ReportController.update",
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", update_400_response()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Create report note",
|
||||||
|
operationId: "AdminAPI.ReportController.notes_create",
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
content: %Schema{type: :string, description: "The message"}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Delete report note",
|
||||||
|
operationId: "AdminAPI.ReportController.notes_delete",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:report_id, :path, :string, "Report ID"),
|
||||||
|
Operation.parameter(:id, :path, :string, "Note ID")
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report_state do
|
||||||
|
%Schema{type: :string, enum: ["open", "closed", "resolved"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, FlakeID, "Report ID",
|
||||||
|
example: "9umDrYheeY451cQnEe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
state: report_state(),
|
||||||
|
account: account_admin(),
|
||||||
|
actor: account_admin(),
|
||||||
|
content: %Schema{type: :string},
|
||||||
|
created_at: %Schema{type: :string, format: :"date-time"},
|
||||||
|
statuses: %Schema{type: :array, items: Status},
|
||||||
|
notes: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
user_id: FlakeID,
|
||||||
|
content: %Schema{type: :string},
|
||||||
|
inserted_at: %Schema{type: :string, format: :"date-time"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp account_admin do
|
||||||
|
%Schema{
|
||||||
|
title: "Account",
|
||||||
|
description: "Account view for admins",
|
||||||
|
type: :object,
|
||||||
|
properties:
|
||||||
|
Map.merge(Account.schema().properties, %{
|
||||||
|
nickname: %Schema{type: :string},
|
||||||
|
deactivated: %Schema{type: :boolean},
|
||||||
|
local: %Schema{type: :boolean},
|
||||||
|
roles: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
admin: %Schema{type: :boolean},
|
||||||
|
moderator: %Schema{type: :boolean}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmation_pending: %Schema{type: :boolean}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:reports],
|
||||||
|
properties: %{
|
||||||
|
reports: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{allOf: [FlakeID], description: "Required, report ID"},
|
||||||
|
state: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Required, the new state. Valid values are `open`, `closed` and `resolved`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"reports" => [
|
||||||
|
%{"id" => "123", "state" => "closed"},
|
||||||
|
%{"id" => "1337", "state" => "resolved"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_400_response do
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{allOf: [FlakeID], description: "Report ID"},
|
||||||
|
error: %Schema{type: :string, description: "Error message"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -74,7 +74,7 @@ def show_operation do
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Status", "application/json", Status),
|
200 => Operation.response("Status", "application/json", status()),
|
||||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ defp status do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp admin_account do
|
def admin_account do
|
||||||
%Schema{
|
%Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
|
|
|
@ -137,7 +137,7 @@ defp instance do
|
||||||
"background_upload_limit" => 4_000_000,
|
"background_upload_limit" => 4_000_000,
|
||||||
"background_image" => "/static/image.png",
|
"background_image" => "/static/image.png",
|
||||||
"banner_upload_limit" => 4_000_000,
|
"banner_upload_limit" => 4_000_000,
|
||||||
"description" => "A Pleroma instance, an alternative fediverse server",
|
"description" => "Pleroma: An efficient and flexible fediverse server",
|
||||||
"email" => "lain@lain.com",
|
"email" => "lain@lain.com",
|
||||||
"languages" => ["en"],
|
"languages" => ["en"],
|
||||||
"max_toot_chars" => 5000,
|
"max_toot_chars" => 5000,
|
||||||
|
|
42
lib/pleroma/web/embed_controller.ex
Normal file
42
lib/pleroma/web/embed_controller.ex
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.EmbedController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
plug(:put_layout, :embed)
|
||||||
|
|
||||||
|
def show(conn, %{"id" => id}) do
|
||||||
|
with %Activity{local: true} = activity <-
|
||||||
|
Activity.get_by_id_with_object(id),
|
||||||
|
true <- Visibility.is_public?(activity.object) do
|
||||||
|
{:ok, author} = User.get_or_fetch(activity.object.data["actor"])
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete_resp_header("x-frame-options")
|
||||||
|
|> delete_resp_header("content-security-policy")
|
||||||
|
|> render("show.html",
|
||||||
|
activity: activity,
|
||||||
|
author: User.sanitize_html(author),
|
||||||
|
counts: get_counts(activity)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_counts(%Activity{} = activity) do
|
||||||
|
%Object{data: data} = Object.normalize(activity)
|
||||||
|
|
||||||
|
%{
|
||||||
|
likes: Map.get(data, "like_count", 0),
|
||||||
|
replies: Map.get(data, "repliesCount", 0),
|
||||||
|
announces: Map.get(data, "announcement_count", 0)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -139,9 +139,7 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||||
def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do
|
def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _params) do
|
||||||
user = original_user
|
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
||||||
|
@ -183,12 +181,31 @@ def update_credentials(%{assigns: %{user: original_user}, body_params: params} =
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
|
|
||||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
|
user
|
||||||
|
|> build_update_activity_params()
|
||||||
|
|> ActivityPub.update()
|
||||||
|
|
||||||
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :forbidden, "Invalid request")
|
_e -> render_error(conn, :forbidden, "Invalid request")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Hotfix, handling will be redone with the pipeline
|
||||||
|
defp build_update_activity_params(user) do
|
||||||
|
object =
|
||||||
|
Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
|
||||||
|
|> Map.delete("@context")
|
||||||
|
|
||||||
|
%{
|
||||||
|
local: true,
|
||||||
|
to: [user.follower_address],
|
||||||
|
cc: [],
|
||||||
|
object: object,
|
||||||
|
actor: user.ap_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
||||||
with true <- is_map(params),
|
with true <- is_map(params),
|
||||||
true <- Map.has_key?(params, params_field),
|
true <- Map.has_key?(params, params_field),
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
|
||||||
|
|
||||||
@doc "GET /api/v1/conversations"
|
@doc "GET /api/v1/conversations"
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
params = stringify_pagination_params(params)
|
||||||
participations = Participation.for_user_with_last_activity_id(user, params)
|
participations = Participation.for_user_with_last_activity_id(user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -36,4 +37,20 @@ def mark_as_read(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
|
||||||
render(conn, "participation.json", participation: participation, for: user)
|
render(conn, "participation.json", participation: participation, for: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp stringify_pagination_params(params) do
|
||||||
|
atom_keys =
|
||||||
|
Pleroma.Pagination.page_keys()
|
||||||
|
|> Enum.map(&String.to_atom(&1))
|
||||||
|
|
||||||
|
str_keys =
|
||||||
|
params
|
||||||
|
|> Map.take(atom_keys)
|
||||||
|
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
params
|
||||||
|
|> Map.delete(atom_keys)
|
||||||
|
|> Map.merge(str_keys)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -113,22 +113,44 @@ defp resource_search(:v2, "hashtags", query, _options) do
|
||||||
query
|
query
|
||||||
|> prepare_tags()
|
|> prepare_tags()
|
||||||
|> Enum.map(fn tag ->
|
|> Enum.map(fn tag ->
|
||||||
tag = String.trim_leading(tag, "#")
|
|
||||||
%{name: tag, url: tags_path <> tag}
|
%{name: tag, url: tags_path <> tag}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v1, "hashtags", query, _options) do
|
defp resource_search(:v1, "hashtags", query, _options) do
|
||||||
query
|
prepare_tags(query)
|
||||||
|> prepare_tags()
|
|
||||||
|> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp prepare_tags(query) do
|
defp prepare_tags(query, add_joined_tag \\ true) do
|
||||||
|
tags =
|
||||||
query
|
query
|
||||||
|> String.split()
|
|> String.split(~r/[^#\w]+/u, trim: true)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq_by(&String.downcase/1)
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|
||||||
|
explicit_tags = Enum.filter(tags, fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|
|
||||||
|
tags =
|
||||||
|
if Enum.any?(explicit_tags) do
|
||||||
|
explicit_tags
|
||||||
|
else
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
|
||||||
|
tags = Enum.map(tags, fn tag -> String.trim_leading(tag, "#") end)
|
||||||
|
|
||||||
|
if Enum.empty?(explicit_tags) && add_joined_tag do
|
||||||
|
tags
|
||||||
|
|> Kernel.++([joined_tag(tags)])
|
||||||
|
|> Enum.uniq_by(&String.downcase/1)
|
||||||
|
else
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp joined_tag(tags) do
|
||||||
|
tags
|
||||||
|
|> Enum.map(fn tag -> String.capitalize(tag) end)
|
||||||
|
|> Enum.join()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_fallback(f, fallback \\ []) do
|
defp with_fallback(f, fallback \\ []) do
|
||||||
|
|
|
@ -111,7 +111,7 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
else
|
else
|
||||||
activities =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put("type", ["Create"])
|
||||||
|> Map.put("local_only", local_only)
|
|> Map.put("local_only", local_only)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put("blocking_user", user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put("muting_user", user)
|
||||||
|
|
|
@ -182,12 +182,14 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
bot = user.actor_type in ["Application", "Service"]
|
bot = user.actor_type in ["Application", "Service"]
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
Enum.map(user.emoji, fn {shortcode, url} ->
|
Enum.map(user.emoji, fn {shortcode, raw_url} ->
|
||||||
|
url = MediaProxy.url(raw_url)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"shortcode" => shortcode,
|
shortcode: shortcode,
|
||||||
"url" => url,
|
url: url,
|
||||||
"static_url" => url,
|
static_url: url,
|
||||||
"visible_in_picker" => false
|
visible_in_picker: false
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,12 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
|
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def changeset(struct, params) do
|
def changeset(struct, params) do
|
||||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
|
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def register_changeset(struct, params \\ %{}) do
|
def register_changeset(struct, params \\ %{}) do
|
||||||
changeset =
|
changeset =
|
||||||
struct
|
struct
|
||||||
|
@ -52,18 +52,19 @@ def register_changeset(struct, params \\ %{}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec create(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def create(params) do
|
def create(params) do
|
||||||
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
|
%__MODULE__{}
|
||||||
Repo.insert(changeset)
|
|> register_changeset(params)
|
||||||
end
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec update(pos_integer(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def update(params) do
|
def update(id, params) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
changeset <- changeset(app, params) do
|
app
|
||||||
Repo.update(changeset)
|
|> changeset(params)
|
||||||
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ def update(params) do
|
||||||
Gets app by attrs or create new with attrs.
|
Gets app by attrs or create new with attrs.
|
||||||
And updates the scopes if need.
|
And updates the scopes if need.
|
||||||
"""
|
"""
|
||||||
@spec get_or_make(map(), list(String.t())) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec get_or_make(map(), list(String.t())) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def get_or_make(attrs, scopes) do
|
def get_or_make(attrs, scopes) do
|
||||||
with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
|
with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
|
||||||
update_scopes(app, scopes)
|
update_scopes(app, scopes)
|
||||||
|
@ -92,7 +93,7 @@ defp update_scopes(%__MODULE__{} = app, scopes) do
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
|
@spec search(map()) :: {:ok, [t()], non_neg_integer()}
|
||||||
def search(params) do
|
def search(params) do
|
||||||
query = from(a in __MODULE__)
|
query = from(a in __MODULE__)
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ def search(params) do
|
||||||
{:ok, Repo.all(query), count}
|
{:ok, Repo.all(query), count}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def destroy(id) do
|
def destroy(id) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
Repo.delete(app)
|
Repo.delete(app)
|
||||||
|
|
|
@ -164,10 +164,10 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/relay", AdminAPIController, :relay_follow)
|
post("/relay", AdminAPIController, :relay_follow)
|
||||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||||
|
|
||||||
post("/users/invite_token", AdminAPIController, :create_invite_token)
|
post("/users/invite_token", InviteController, :create)
|
||||||
get("/users/invites", AdminAPIController, :invites)
|
get("/users/invites", InviteController, :index)
|
||||||
post("/users/revoke_invite", AdminAPIController, :revoke_invite)
|
post("/users/revoke_invite", InviteController, :revoke)
|
||||||
post("/users/email_invite", AdminAPIController, :email_invite)
|
post("/users/email_invite", InviteController, :email)
|
||||||
|
|
||||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||||
|
@ -183,11 +183,11 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||||
|
|
||||||
get("/reports", AdminAPIController, :list_reports)
|
get("/reports", ReportController, :index)
|
||||||
get("/reports/:id", AdminAPIController, :report_show)
|
get("/reports/:id", ReportController, :show)
|
||||||
patch("/reports", AdminAPIController, :reports_update)
|
patch("/reports", ReportController, :update)
|
||||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
post("/reports/:id/notes", ReportController, :notes_create)
|
||||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
||||||
|
|
||||||
get("/statuses/:id", StatusController, :show)
|
get("/statuses/:id", StatusController, :show)
|
||||||
put("/statuses/:id", StatusController, :update)
|
put("/statuses/:id", StatusController, :update)
|
||||||
|
@ -205,10 +205,10 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
get("/stats", AdminAPIController, :stats)
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
get("/oauth_app", AdminAPIController, :oauth_app_list)
|
get("/oauth_app", OAuthAppController, :index)
|
||||||
post("/oauth_app", AdminAPIController, :oauth_app_create)
|
post("/oauth_app", OAuthAppController, :create)
|
||||||
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
|
patch("/oauth_app/:id", OAuthAppController, :update)
|
||||||
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
|
delete("/oauth_app/:id", OAuthAppController, :delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -664,6 +664,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/auth/password", MastodonAPI.AuthController, :password_reset)
|
post("/auth/password", MastodonAPI.AuthController, :password_reset)
|
||||||
|
|
||||||
get("/web/*path", MastoFEController, :index)
|
get("/web/*path", MastoFEController, :index)
|
||||||
|
|
||||||
|
get("/embed/:id", EmbedController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web.MediaProxy do
|
scope "/proxy/", Pleroma.Web.MediaProxy do
|
||||||
|
|
|
@ -136,7 +136,7 @@ def filtered_by_user?(%User{} = user, %Activity{} = item) do
|
||||||
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
|
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
|
||||||
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, parent_host),
|
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, parent_host),
|
||||||
true <- thread_containment(item, user),
|
true <- thread_containment(item, user),
|
||||||
false <- CommonAPI.thread_muted?(user, item) do
|
false <- CommonAPI.thread_muted?(user, parent) do
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
_ -> true
|
_ -> true
|
||||||
|
|
8
lib/pleroma/web/templates/embed/_attachment.html.eex
Normal file
8
lib/pleroma/web/templates/embed/_attachment.html.eex
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<%= case @mediaType do %>
|
||||||
|
<% "audio" -> %>
|
||||||
|
<audio src="<%= @url %>" controls="controls"></audio>
|
||||||
|
<% "video" -> %>
|
||||||
|
<video src="<%= @url %>" controls="controls"></video>
|
||||||
|
<% _ -> %>
|
||||||
|
<img src="<%= @url %>" alt="<%= @name %>" title="<%= @name %>">
|
||||||
|
<% end %>
|
76
lib/pleroma/web/templates/embed/show.html.eex
Normal file
76
lib/pleroma/web/templates/embed/show.html.eex
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<div>
|
||||||
|
<div class="p-author h-card">
|
||||||
|
<a class="u-url" rel="author noopener" href="<%= @author.ap_id %>">
|
||||||
|
<div class="avatar">
|
||||||
|
<img src="<%= User.avatar_url(@author) |> MediaProxy.url %>" width="48" height="48" alt="">
|
||||||
|
</div>
|
||||||
|
<span class="display-name" style="padding-left: 0.5em;">
|
||||||
|
<bdi><%= raw (@author.name |> Formatter.emojify(@author.emoji)) %></bdi>
|
||||||
|
<span class="nickname"><%= full_nickname(@author) %></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-content" >
|
||||||
|
<%= if status_title(@activity) != "" do %>
|
||||||
|
<details <%= if open_content?() do %>open<% end %>>
|
||||||
|
<summary><%= raw status_title(@activity) %></summary>
|
||||||
|
<div><%= activity_content(@activity) %></div>
|
||||||
|
</details>
|
||||||
|
<% else %>
|
||||||
|
<div><%= activity_content(@activity) %></div>
|
||||||
|
<% end %>
|
||||||
|
<%= for %{"name" => name, "url" => [url | _]} <- attachments(@activity) do %>
|
||||||
|
<div class="attachment">
|
||||||
|
<%= if sensitive?(@activity) do %>
|
||||||
|
<details class="nsfw">
|
||||||
|
<summary onClick="updateHeight()"><%= Gettext.gettext("sensitive media") %></summary>
|
||||||
|
<div class="nsfw-content">
|
||||||
|
<%= render("_attachment.html", %{name: name, url: url["href"],
|
||||||
|
mediaType: fetch_media_type(url)}) %>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<% else %>
|
||||||
|
<%= render("_attachment.html", %{name: name, url: url["href"],
|
||||||
|
mediaType: fetch_media_type(url)}) %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dl class="counts pull-right">
|
||||||
|
<dt><%= Gettext.gettext("replies") %></dt><dd><%= @counts.replies %></dd>
|
||||||
|
<dt><%= Gettext.gettext("announces") %></dt><dd><%= @counts.announces %></dd>
|
||||||
|
<dt><%= Gettext.gettext("likes") %></dt><dd><%= @counts.likes %></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<p class="date pull-left">
|
||||||
|
<%= link published(@activity), to: activity_url(@author, @activity) %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updateHeight() {
|
||||||
|
window.requestAnimationFrame(function(){
|
||||||
|
var height = document.getElementsByTagName('html')[0].scrollHeight;
|
||||||
|
|
||||||
|
window.parent.postMessage({
|
||||||
|
type: 'setHeightPleromaEmbed',
|
||||||
|
id: window.parentId,
|
||||||
|
height: height,
|
||||||
|
}, '*');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', function(e){
|
||||||
|
var data = e.data || {};
|
||||||
|
|
||||||
|
if (!window.parent || data.type !== 'setHeightPleromaEmbed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.parentId = data.id
|
||||||
|
|
||||||
|
updateHeight()
|
||||||
|
});
|
||||||
|
</script>
|
15
lib/pleroma/web/templates/layout/embed.html.eex
Normal file
15
lib/pleroma/web/templates/layout/embed.html.eex
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
|
||||||
|
<title><%= Pleroma.Config.get([:instance, :name]) %></title>
|
||||||
|
<meta content='noindex' name='robots'>
|
||||||
|
<%= Phoenix.HTML.raw(assigns[:meta] || "") %>
|
||||||
|
<link rel="stylesheet" href="/embed.css">
|
||||||
|
<base target="_parent">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= render @view_module, @view_template, assigns %>
|
||||||
|
</body>
|
||||||
|
</html>
|
74
lib/pleroma/web/views/embed_view.ex
Normal file
74
lib/pleroma/web/views/embed_view.ex
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.EmbedView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Calendar.Strftime
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Emoji.Formatter
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Gettext
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.Metadata.Utils
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
|
use Phoenix.HTML
|
||||||
|
|
||||||
|
@media_types ["image", "audio", "video"]
|
||||||
|
|
||||||
|
defp fetch_media_type(%{"mediaType" => mediaType}) do
|
||||||
|
Utils.fetch_media_type(@media_types, mediaType)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp open_content? do
|
||||||
|
Pleroma.Config.get(
|
||||||
|
[:frontend_configurations, :collapse_message_with_subjects],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp full_nickname(user) do
|
||||||
|
%{host: host} = URI.parse(user.ap_id)
|
||||||
|
"@" <> user.nickname <> "@" <> host
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_title(%Activity{object: %Object{data: %{"name" => name}}}) when is_binary(name),
|
||||||
|
do: name
|
||||||
|
|
||||||
|
defp status_title(%Activity{object: %Object{data: %{"summary" => summary}}})
|
||||||
|
when is_binary(summary),
|
||||||
|
do: summary
|
||||||
|
|
||||||
|
defp status_title(_), do: nil
|
||||||
|
|
||||||
|
defp activity_content(%Activity{object: %Object{data: %{"content" => content}}}) do
|
||||||
|
content |> Pleroma.HTML.filter_tags() |> raw()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp activity_content(_), do: nil
|
||||||
|
|
||||||
|
defp activity_url(%User{local: true}, activity) do
|
||||||
|
Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp activity_url(%User{local: false}, %Activity{object: %Object{data: data}}) do
|
||||||
|
data["url"] || data["external_url"] || data["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp attachments(%Activity{object: %Object{data: %{"attachment" => attachments}}}) do
|
||||||
|
attachments
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sensitive?(%Activity{object: %Object{data: %{"sensitive" => sensitive}}}) do
|
||||||
|
sensitive
|
||||||
|
end
|
||||||
|
|
||||||
|
defp published(%Activity{object: %Object{data: %{"published" => published}}}) do
|
||||||
|
published
|
||||||
|
|> NaiveDateTime.from_iso8601!()
|
||||||
|
|> Strftime.strftime!("%B %d, %Y, %l:%M %p")
|
||||||
|
end
|
||||||
|
end
|
10
mix.lock
10
mix.lock
|
@ -12,7 +12,7 @@
|
||||||
"calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "738d0e17a93c2ccfe4ddc707bdc8e672e9074c8569498483feb1c4530fb91b2b"},
|
"calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "738d0e17a93c2ccfe4ddc707bdc8e672e9074c8569498483feb1c4530fb91b2b"},
|
||||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
||||||
"castore": {:hex, :castore, "0.1.5", "591c763a637af2cc468a72f006878584bc6c306f8d111ef8ba1d4c10e0684010", [:mix], [], "hexpm", "6db356b2bc6cc22561e051ff545c20ad064af57647e436650aa24d7d06cd941a"},
|
"castore": {:hex, :castore, "0.1.5", "591c763a637af2cc468a72f006878584bc6c306f8d111ef8ba1d4c10e0684010", [:mix], [], "hexpm", "6db356b2bc6cc22561e051ff545c20ad064af57647e436650aa24d7d06cd941a"},
|
||||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
|
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"},
|
"comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"},
|
||||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
|
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
|
||||||
|
@ -50,12 +50,12 @@
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||||
"gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"},
|
"gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"},
|
||||||
"gun": {:git, "https://github.com/ninenines/gun.git", "e1a69b36b180a574c0ac314ced9613fdd52312cc", [ref: "e1a69b36b180a574c0ac314ced9613fdd52312cc"]},
|
"gun": {:git, "https://github.com/ninenines/gun.git", "e1a69b36b180a574c0ac314ced9613fdd52312cc", [ref: "e1a69b36b180a574c0ac314ced9613fdd52312cc"]},
|
||||||
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"},
|
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||||
"html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
|
"html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
|
||||||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]},
|
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]},
|
||||||
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
||||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
|
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
||||||
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||||
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
"recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"},
|
"recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"},
|
||||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
||||||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
||||||
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"},
|
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"},
|
||||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||||
|
@ -112,7 +112,7 @@
|
||||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||||
"tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
|
"tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
|
||||||
"ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "db9fbfb5ac707bc4f85a297758406340bf0358b4af737a88113c1a9eee120ac7"},
|
"ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "db9fbfb5ac707bc4f85a297758406340bf0358b4af737a88113c1a9eee120ac7"},
|
||||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
||||||
"web_push_encryption": {:hex, :web_push_encryption, "0.2.3", "a0ceab85a805a30852f143d22d71c434046fbdbafbc7292e7887cec500826a80", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "9315c8f37c108835cf3f8e9157d7a9b8f420a34f402d1b1620a31aed5b93ecdf"},
|
"web_push_encryption": {:hex, :web_push_encryption, "0.2.3", "a0ceab85a805a30852f143d22d71c434046fbdbafbc7292e7887cec500826a80", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "9315c8f37c108835cf3f8e9157d7a9b8f420a34f402d1b1620a31aed5b93ecdf"},
|
||||||
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
|
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
|
||||||
|
|
|
@ -3,14 +3,16 @@ msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
|
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: 2020-06-02 07:36+0000\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Fristi <fristi@subcon.town>\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: Dutch <https://translate.pleroma.social/projects/pleroma/"
|
||||||
|
"pleroma/nl/>\n"
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Translate Toolkit 2.5.1\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
|
"X-Generator: Weblate 4.0.4\n"
|
||||||
|
|
||||||
## This file is a PO Template file.
|
## This file is a PO Template file.
|
||||||
##
|
##
|
||||||
|
@ -23,142 +25,142 @@ msgstr ""
|
||||||
## effect: edit them in PO (`.po`) files instead.
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
## From Ecto.Changeset.cast/4
|
## From Ecto.Changeset.cast/4
|
||||||
msgid "can't be blank"
|
msgid "can't be blank"
|
||||||
msgstr ""
|
msgstr "kan niet leeg zijn"
|
||||||
|
|
||||||
## From Ecto.Changeset.unique_constraint/3
|
## From Ecto.Changeset.unique_constraint/3
|
||||||
msgid "has already been taken"
|
msgid "has already been taken"
|
||||||
msgstr ""
|
msgstr "is al bezet"
|
||||||
|
|
||||||
## From Ecto.Changeset.put_change/3
|
## From Ecto.Changeset.put_change/3
|
||||||
msgid "is invalid"
|
msgid "is invalid"
|
||||||
msgstr ""
|
msgstr "is ongeldig"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_format/3
|
## From Ecto.Changeset.validate_format/3
|
||||||
msgid "has invalid format"
|
msgid "has invalid format"
|
||||||
msgstr ""
|
msgstr "heeft een ongeldig formaat"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_subset/3
|
## From Ecto.Changeset.validate_subset/3
|
||||||
msgid "has an invalid entry"
|
msgid "has an invalid entry"
|
||||||
msgstr ""
|
msgstr "heeft een ongeldige entry"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_exclusion/3
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
msgid "is reserved"
|
msgid "is reserved"
|
||||||
msgstr ""
|
msgstr "is gereserveerd"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_confirmation/3
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
msgid "does not match confirmation"
|
msgid "does not match confirmation"
|
||||||
msgstr ""
|
msgstr "komt niet overeen met bevestiging"
|
||||||
|
|
||||||
## From Ecto.Changeset.no_assoc_constraint/3
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
msgid "is still associated with this entry"
|
msgid "is still associated with this entry"
|
||||||
msgstr ""
|
msgstr "is nog geassocieerd met deze entry"
|
||||||
|
|
||||||
msgid "are still associated with this entry"
|
msgid "are still associated with this entry"
|
||||||
msgstr ""
|
msgstr "zijn nog geassocieerd met deze entry"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_length/3
|
## From Ecto.Changeset.validate_length/3
|
||||||
msgid "should be %{count} character(s)"
|
msgid "should be %{count} character(s)"
|
||||||
msgid_plural "should be %{count} character(s)"
|
msgid_plural "should be %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have %{count} item(s)"
|
msgid "should have %{count} item(s)"
|
||||||
msgid_plural "should have %{count} item(s)"
|
msgid_plural "should have %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient %{count} items te bevatten"
|
||||||
|
|
||||||
msgid "should be at least %{count} character(s)"
|
msgid "should be at least %{count} character(s)"
|
||||||
msgid_plural "should be at least %{count} character(s)"
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient ten minste %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient ten minste %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have at least %{count} item(s)"
|
msgid "should have at least %{count} item(s)"
|
||||||
msgid_plural "should have at least %{count} item(s)"
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient ten minste %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient ten minste %{count} items te bevatten"
|
||||||
|
|
||||||
msgid "should be at most %{count} character(s)"
|
msgid "should be at most %{count} character(s)"
|
||||||
msgid_plural "should be at most %{count} character(s)"
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient niet meer dan %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient niet meer dan %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have at most %{count} item(s)"
|
msgid "should have at most %{count} item(s)"
|
||||||
msgid_plural "should have at most %{count} item(s)"
|
msgid_plural "should have at most %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient niet meer dan %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient niet meer dan %{count} items te bevatten"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_number/3
|
## From Ecto.Changeset.validate_number/3
|
||||||
msgid "must be less than %{number}"
|
msgid "must be less than %{number}"
|
||||||
msgstr ""
|
msgstr "dient kleiner te zijn dan %{number}"
|
||||||
|
|
||||||
msgid "must be greater than %{number}"
|
msgid "must be greater than %{number}"
|
||||||
msgstr ""
|
msgstr "dient groter te zijn dan %{number}"
|
||||||
|
|
||||||
msgid "must be less than or equal to %{number}"
|
msgid "must be less than or equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient kleiner dan of gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
msgid "must be greater than or equal to %{number}"
|
msgid "must be greater than or equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient groter dan of gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
msgid "must be equal to %{number}"
|
msgid "must be equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:421
|
#: lib/pleroma/web/common_api/common_api.ex:421
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Account not found"
|
msgid "Account not found"
|
||||||
msgstr ""
|
msgstr "Account niet gevonden"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:249
|
#: lib/pleroma/web/common_api/common_api.ex:249
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Already voted"
|
msgid "Already voted"
|
||||||
msgstr ""
|
msgstr "Al gestemd"
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Bad request"
|
msgid "Bad request"
|
||||||
msgstr ""
|
msgstr "Bad request"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't delete object"
|
msgid "Can't delete object"
|
||||||
msgstr ""
|
msgstr "Object kan niet verwijderd worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't delete this post"
|
msgid "Can't delete this post"
|
||||||
msgstr ""
|
msgstr "Bericht kan niet verwijderd worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/controller_helper.ex:95
|
#: lib/pleroma/web/controller_helper.ex:95
|
||||||
#: lib/pleroma/web/controller_helper.ex:101
|
#: lib/pleroma/web/controller_helper.ex:101
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't display this activity"
|
msgid "Can't display this activity"
|
||||||
msgstr ""
|
msgstr "Activiteit kan niet worden getoond"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't find user"
|
msgid "Can't find user"
|
||||||
msgstr ""
|
msgstr "Gebruiker kan niet gevonden worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't get favorites"
|
msgid "Can't get favorites"
|
||||||
msgstr ""
|
msgstr "Favorieten konden niet opgehaald worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't like object"
|
msgid "Can't like object"
|
||||||
msgstr ""
|
msgstr "Object kan niet geliked worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:556
|
#: lib/pleroma/web/common_api/utils.ex:556
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Cannot post an empty status without attachments"
|
msgid "Cannot post an empty status without attachments"
|
||||||
msgstr ""
|
msgstr "Status kan niet geplaatst worden zonder tekst of bijlagen"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:504
|
#: lib/pleroma/web/common_api/utils.ex:504
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Comment must be up to %{max_size} characters"
|
msgid "Comment must be up to %{max_size} characters"
|
||||||
msgstr ""
|
msgstr "Opmerking dient maximaal %{max_size} karakters te bevatten"
|
||||||
|
|
||||||
#: lib/pleroma/config/config_db.ex:222
|
#: lib/pleroma/config/config_db.ex:222
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddRecipientsContainBlockedDomainsFunction do
|
||||||
|
use Ecto.Migration
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def up do
|
||||||
|
statement = """
|
||||||
|
CREATE OR REPLACE FUNCTION recipients_contain_blocked_domains(recipients varchar[], blocked_domains varchar[]) RETURNS boolean AS $$
|
||||||
|
DECLARE
|
||||||
|
recipient_domain varchar;
|
||||||
|
recipient varchar;
|
||||||
|
BEGIN
|
||||||
|
FOREACH recipient IN ARRAY recipients LOOP
|
||||||
|
recipient_domain = split_part(recipient, '/', 3)::varchar;
|
||||||
|
|
||||||
|
IF recipient_domain = ANY(blocked_domains) THEN
|
||||||
|
RETURN TRUE;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
RETURN FALSE;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(statement)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute(
|
||||||
|
"drop function if exists recipients_contain_blocked_domains(recipients varchar[], blocked_domains varchar[])"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
115
priv/static/embed.css
Normal file
115
priv/static/embed.css
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
body {
|
||||||
|
background-color: #282c37;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar img {
|
||||||
|
float: left;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-content {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a,
|
||||||
|
.counts {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counts dt,
|
||||||
|
.counts dd {
|
||||||
|
float: left;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card {
|
||||||
|
min-height: 48px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
padding-top: 4px;
|
||||||
|
display: block;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* keep emoji from being hilariously huge */
|
||||||
|
.display-name img {
|
||||||
|
max-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name .nickname {
|
||||||
|
padding-top: 4px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nickname:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
margin: 0;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
background-color: #419bdd;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #61a6d9;
|
||||||
|
}
|
43
priv/static/embed.js
Normal file
43
priv/static/embed.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(function () {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var ready = function (loaded) {
|
||||||
|
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
|
||||||
|
loaded()
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', loaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(function () {
|
||||||
|
var iframes = []
|
||||||
|
|
||||||
|
window.addEventListener('message', function (e) {
|
||||||
|
var data = e.data || {}
|
||||||
|
|
||||||
|
if (data.type !== 'setHeightPleromaEmbed' || !iframes[data.id]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iframes[data.id].height = data.height
|
||||||
|
});
|
||||||
|
|
||||||
|
[].forEach.call(document.querySelectorAll('iframe.pleroma-embed'), function (iframe) {
|
||||||
|
iframe.scrolling = 'no'
|
||||||
|
iframe.style.overflow = 'hidden'
|
||||||
|
|
||||||
|
iframes.push(iframe)
|
||||||
|
|
||||||
|
var id = iframes.length - 1
|
||||||
|
|
||||||
|
iframe.onload = function () {
|
||||||
|
iframe.contentWindow.postMessage({
|
||||||
|
type: 'setHeightPleromaEmbed',
|
||||||
|
id: id
|
||||||
|
}, '*')
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.onload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})()
|
|
@ -31,17 +31,5 @@ test "respect connection opts and no proxy", %{uri: uri} do
|
||||||
assert opts[:b] == 1
|
assert opts[:b] == 1
|
||||||
refute Keyword.has_key?(opts, :proxy)
|
refute Keyword.has_key?(opts, :proxy)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "add opts for https" do
|
|
||||||
uri = URI.parse("https://domain.com")
|
|
||||||
|
|
||||||
opts = Hackney.options(uri)
|
|
||||||
|
|
||||||
assert opts[:ssl_options] == [
|
|
||||||
partial_chain: &:hackney_connect.partial_chain/1,
|
|
||||||
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
|
|
||||||
server_name_indication: 'domain.com'
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,7 +67,7 @@ test "it sends `report-to` & `report-uri` CSP response headers" do
|
||||||
|
|
||||||
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
||||||
|
|
||||||
assert csp =~ ~r|report-uri https://endpoint.com; report-to csp-endpoint;|
|
assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;|
|
||||||
|
|
||||||
[reply_to] = Conn.get_resp_header(conn, "reply-to")
|
[reply_to] = Conn.get_resp_header(conn, "reply-to")
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,8 @@ test "relay is unfollowed" do
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor_id" => follower_id,
|
"actor_id" => follower_id,
|
||||||
"limit" => 1,
|
"limit" => 1,
|
||||||
"skip_preload" => true
|
"skip_preload" => true,
|
||||||
|
"invisible_actors" => true
|
||||||
})
|
})
|
||||||
|
|
||||||
assert undo_activity.data["type"] == "Undo"
|
assert undo_activity.data["type"] == "Undo"
|
||||||
|
|
|
@ -1802,7 +1802,7 @@ test "avatar fallback" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
assert User.avatar_url(user) =~ "/images/avi.png"
|
assert User.avatar_url(user) =~ "/images/avi.png"
|
||||||
|
|
||||||
Pleroma.Config.put([:assets, :default_user_avatar], "avatar.png")
|
clear_config([:assets, :default_user_avatar], "avatar.png")
|
||||||
|
|
||||||
user = User.get_cached_by_nickname_or_id(user.nickname)
|
user = User.get_cached_by_nickname_or_id(user.nickname)
|
||||||
assert User.avatar_url(user) =~ "avatar.png"
|
assert User.avatar_url(user) =~ "avatar.png"
|
||||||
|
|
|
@ -108,6 +108,7 @@ test "returns error when object is unknown" do
|
||||||
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
||||||
assert activity.data["type"] == "Announce"
|
assert activity.data["type"] == "Announce"
|
||||||
assert activity.data["actor"] == service_actor.ap_id
|
assert activity.data["actor"] == service_actor.ap_id
|
||||||
|
assert activity.data["to"] == [service_actor.follower_address]
|
||||||
assert called(Pleroma.Web.Federator.publish(activity))
|
assert called(Pleroma.Web.Federator.publish(activity))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ReportNote
|
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
@ -587,122 +585,6 @@ test "/:right DELETE, can remove from a permission group (multiple)", %{
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/email_invite, with valid config" do
|
|
||||||
setup do: clear_config([:instance, :registrations_open], false)
|
|
||||||
setup do: clear_config([:instance, :invites_enabled], true)
|
|
||||||
|
|
||||||
test "sends invitation and returns 204", %{admin: admin, conn: conn} do
|
|
||||||
recipient_email = "foo@bar.com"
|
|
||||||
recipient_name = "J. D."
|
|
||||||
|
|
||||||
conn =
|
|
||||||
post(
|
|
||||||
conn,
|
|
||||||
"/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert json_response(conn, :no_content)
|
|
||||||
|
|
||||||
token_record = List.last(Repo.all(Pleroma.UserInviteToken))
|
|
||||||
assert token_record
|
|
||||||
refute token_record.used
|
|
||||||
|
|
||||||
notify_email = Config.get([:instance, :notify_email])
|
|
||||||
instance_name = Config.get([:instance, :name])
|
|
||||||
|
|
||||||
email =
|
|
||||||
Pleroma.Emails.UserEmail.user_invitation_email(
|
|
||||||
admin,
|
|
||||||
token_record,
|
|
||||||
recipient_email,
|
|
||||||
recipient_name
|
|
||||||
)
|
|
||||||
|
|
||||||
Swoosh.TestAssertions.assert_email_sent(
|
|
||||||
from: {instance_name, notify_email},
|
|
||||||
to: {recipient_name, recipient_email},
|
|
||||||
html_body: email.html_body
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns 403 if requested by a non-admin" do
|
|
||||||
non_admin_user = insert(:user)
|
|
||||||
token = insert(:oauth_token, user: non_admin_user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> assign(:user, non_admin_user)
|
|
||||||
|> assign(:token, token)
|
|
||||||
|> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
|
||||||
|
|
||||||
assert json_response(conn, :forbidden)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "email with +", %{conn: conn, admin: admin} do
|
|
||||||
recipient_email = "foo+bar@baz.com"
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json;charset=utf-8")
|
|
||||||
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
|
|
||||||
|> json_response(:no_content)
|
|
||||||
|
|
||||||
token_record =
|
|
||||||
Pleroma.UserInviteToken
|
|
||||||
|> Repo.all()
|
|
||||||
|> List.last()
|
|
||||||
|
|
||||||
assert token_record
|
|
||||||
refute token_record.used
|
|
||||||
|
|
||||||
notify_email = Config.get([:instance, :notify_email])
|
|
||||||
instance_name = Config.get([:instance, :name])
|
|
||||||
|
|
||||||
email =
|
|
||||||
Pleroma.Emails.UserEmail.user_invitation_email(
|
|
||||||
admin,
|
|
||||||
token_record,
|
|
||||||
recipient_email
|
|
||||||
)
|
|
||||||
|
|
||||||
Swoosh.TestAssertions.assert_email_sent(
|
|
||||||
from: {instance_name, notify_email},
|
|
||||||
to: recipient_email,
|
|
||||||
html_body: email.html_body
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
|
|
||||||
setup do: clear_config([:instance, :registrations_open])
|
|
||||||
setup do: clear_config([:instance, :invites_enabled])
|
|
||||||
|
|
||||||
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
|
|
||||||
Config.put([:instance, :registrations_open], false)
|
|
||||||
Config.put([:instance, :invites_enabled], false)
|
|
||||||
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
|
||||||
|
|
||||||
assert json_response(conn, :bad_request) ==
|
|
||||||
%{
|
|
||||||
"error" =>
|
|
||||||
"To send invites you need to set the `invites_enabled` option to true."
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
|
||||||
Config.put([:instance, :registrations_open], true)
|
|
||||||
Config.put([:instance, :invites_enabled], true)
|
|
||||||
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
|
||||||
|
|
||||||
assert json_response(conn, :bad_request) ==
|
|
||||||
%{
|
|
||||||
"error" =>
|
|
||||||
"To send invites you need to set the `registrations_open` option to false."
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
|
test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -756,8 +638,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "pagination works correctly with service users", %{conn: conn} do
|
test "pagination works correctly with service users", %{conn: conn} do
|
||||||
service1 = insert(:user, ap_id: Web.base_url() <> "/relay")
|
service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
|
||||||
service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch")
|
|
||||||
insert_list(25, :user)
|
insert_list(25, :user)
|
||||||
|
|
||||||
assert %{"count" => 26, "page_size" => 10, "users" => users1} =
|
assert %{"count" => 26, "page_size" => 10, "users" => users1} =
|
||||||
|
@ -766,8 +648,7 @@ test "pagination works correctly with service users", %{conn: conn} do
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert Enum.count(users1) == 10
|
assert Enum.count(users1) == 10
|
||||||
assert service1 not in [users1]
|
assert service1 not in users1
|
||||||
assert service2 not in [users1]
|
|
||||||
|
|
||||||
assert %{"count" => 26, "page_size" => 10, "users" => users2} =
|
assert %{"count" => 26, "page_size" => 10, "users" => users2} =
|
||||||
conn
|
conn
|
||||||
|
@ -775,8 +656,7 @@ test "pagination works correctly with service users", %{conn: conn} do
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert Enum.count(users2) == 10
|
assert Enum.count(users2) == 10
|
||||||
assert service1 not in [users2]
|
assert service1 not in users2
|
||||||
assert service2 not in [users2]
|
|
||||||
|
|
||||||
assert %{"count" => 26, "page_size" => 10, "users" => users3} =
|
assert %{"count" => 26, "page_size" => 10, "users" => users3} =
|
||||||
conn
|
conn
|
||||||
|
@ -784,8 +664,7 @@ test "pagination works correctly with service users", %{conn: conn} do
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert Enum.count(users3) == 6
|
assert Enum.count(users3) == 6
|
||||||
assert service1 not in [users3]
|
assert service1 not in users3
|
||||||
assert service2 not in [users3]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders empty array for the second page", %{conn: conn} do
|
test "renders empty array for the second page", %{conn: conn} do
|
||||||
|
@ -1317,392 +1196,6 @@ test "returns 404 if user not found", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/users/invite_token" do
|
|
||||||
test "without options", %{conn: conn} do
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/invite_token")
|
|
||||||
|
|
||||||
invite_json = json_response(conn, 200)
|
|
||||||
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
|
||||||
refute invite.used
|
|
||||||
refute invite.expires_at
|
|
||||||
refute invite.max_use
|
|
||||||
assert invite.invite_type == "one_time"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with expires_at", %{conn: conn} do
|
|
||||||
conn =
|
|
||||||
post(conn, "/api/pleroma/admin/users/invite_token", %{
|
|
||||||
"expires_at" => Date.to_string(Date.utc_today())
|
|
||||||
})
|
|
||||||
|
|
||||||
invite_json = json_response(conn, 200)
|
|
||||||
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
|
||||||
|
|
||||||
refute invite.used
|
|
||||||
assert invite.expires_at == Date.utc_today()
|
|
||||||
refute invite.max_use
|
|
||||||
assert invite.invite_type == "date_limited"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with max_use", %{conn: conn} do
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
|
|
||||||
|
|
||||||
invite_json = json_response(conn, 200)
|
|
||||||
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
|
||||||
refute invite.used
|
|
||||||
refute invite.expires_at
|
|
||||||
assert invite.max_use == 150
|
|
||||||
assert invite.invite_type == "reusable"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with max use and expires_at", %{conn: conn} do
|
|
||||||
conn =
|
|
||||||
post(conn, "/api/pleroma/admin/users/invite_token", %{
|
|
||||||
"max_use" => 150,
|
|
||||||
"expires_at" => Date.to_string(Date.utc_today())
|
|
||||||
})
|
|
||||||
|
|
||||||
invite_json = json_response(conn, 200)
|
|
||||||
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
|
||||||
refute invite.used
|
|
||||||
assert invite.expires_at == Date.utc_today()
|
|
||||||
assert invite.max_use == 150
|
|
||||||
assert invite.invite_type == "reusable_date_limited"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/users/invites" do
|
|
||||||
test "no invites", %{conn: conn} do
|
|
||||||
conn = get(conn, "/api/pleroma/admin/users/invites")
|
|
||||||
|
|
||||||
assert json_response(conn, 200) == %{"invites" => []}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with invite", %{conn: conn} do
|
|
||||||
{:ok, invite} = UserInviteToken.create_invite()
|
|
||||||
|
|
||||||
conn = get(conn, "/api/pleroma/admin/users/invites")
|
|
||||||
|
|
||||||
assert json_response(conn, 200) == %{
|
|
||||||
"invites" => [
|
|
||||||
%{
|
|
||||||
"expires_at" => nil,
|
|
||||||
"id" => invite.id,
|
|
||||||
"invite_type" => "one_time",
|
|
||||||
"max_use" => nil,
|
|
||||||
"token" => invite.token,
|
|
||||||
"used" => false,
|
|
||||||
"uses" => 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/users/revoke_invite" do
|
|
||||||
test "with token", %{conn: conn} do
|
|
||||||
{:ok, invite} = UserInviteToken.create_invite()
|
|
||||||
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
|
|
||||||
|
|
||||||
assert json_response(conn, 200) == %{
|
|
||||||
"expires_at" => nil,
|
|
||||||
"id" => invite.id,
|
|
||||||
"invite_type" => "one_time",
|
|
||||||
"max_use" => nil,
|
|
||||||
"token" => invite.token,
|
|
||||||
"used" => true,
|
|
||||||
"uses" => 0
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with invalid token", %{conn: conn} do
|
|
||||||
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
|
|
||||||
|
|
||||||
assert json_response(conn, :not_found) == %{"error" => "Not found"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/reports/:id" do
|
|
||||||
test "returns report by its id", %{conn: conn} do
|
|
||||||
[reporter, target_user] = insert_pair(:user)
|
|
||||||
activity = insert(:note_activity, user: target_user)
|
|
||||||
|
|
||||||
{:ok, %{id: report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports/#{report_id}")
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response["id"] == report_id
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 404 when report id is invalid", %{conn: conn} do
|
|
||||||
conn = get(conn, "/api/pleroma/admin/reports/test")
|
|
||||||
|
|
||||||
assert json_response(conn, :not_found) == %{"error" => "Not found"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PATCH /api/pleroma/admin/reports" do
|
|
||||||
setup do
|
|
||||||
[reporter, target_user] = insert_pair(:user)
|
|
||||||
activity = insert(:note_activity, user: target_user)
|
|
||||||
|
|
||||||
{:ok, %{id: report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, %{id: second_report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel very offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
%{
|
|
||||||
id: report_id,
|
|
||||||
second_report_id: second_report_id
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
|
|
||||||
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
|
||||||
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> assign(:token, read_token)
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [%{"state" => "resolved", "id" => id}]
|
|
||||||
})
|
|
||||||
|> json_response(403)
|
|
||||||
|
|
||||||
assert response == %{
|
|
||||||
"error" => "Insufficient permissions: admin:write:reports."
|
|
||||||
}
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> assign(:token, write_token)
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [%{"state" => "resolved", "id" => id}]
|
|
||||||
})
|
|
||||||
|> json_response(:no_content)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [
|
|
||||||
%{"state" => "resolved", "id" => id}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|> json_response(:no_content)
|
|
||||||
|
|
||||||
activity = Activity.get_by_id(id)
|
|
||||||
assert activity.data["state"] == "resolved"
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "closes report", %{conn: conn, id: id, admin: admin} do
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [
|
|
||||||
%{"state" => "closed", "id" => id}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|> json_response(:no_content)
|
|
||||||
|
|
||||||
activity = Activity.get_by_id(id)
|
|
||||||
assert activity.data["state"] == "closed"
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} updated report ##{id} with 'closed' state"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 400 when state is unknown", %{conn: conn, id: id} do
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [
|
|
||||||
%{"state" => "test", "id" => id}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 404 when report is not exist", %{conn: conn} do
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [
|
|
||||||
%{"state" => "closed", "id" => "test"}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
assert hd(json_response(conn, :bad_request))["error"] == "not_found"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates state of multiple reports", %{
|
|
||||||
conn: conn,
|
|
||||||
id: id,
|
|
||||||
admin: admin,
|
|
||||||
second_report_id: second_report_id
|
|
||||||
} do
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/reports", %{
|
|
||||||
"reports" => [
|
|
||||||
%{"state" => "resolved", "id" => id},
|
|
||||||
%{"state" => "closed", "id" => second_report_id}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|> json_response(:no_content)
|
|
||||||
|
|
||||||
activity = Activity.get_by_id(id)
|
|
||||||
second_activity = Activity.get_by_id(second_report_id)
|
|
||||||
assert activity.data["state"] == "resolved"
|
|
||||||
assert second_activity.data["state"] == "closed"
|
|
||||||
|
|
||||||
[first_log_entry, second_log_entry] = Repo.all(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(first_log_entry) ==
|
|
||||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(second_log_entry) ==
|
|
||||||
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/reports" do
|
|
||||||
test "returns empty response when no reports created", %{conn: conn} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports")
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert Enum.empty?(response["reports"])
|
|
||||||
assert response["total"] == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns reports", %{conn: conn} do
|
|
||||||
[reporter, target_user] = insert_pair(:user)
|
|
||||||
activity = insert(:note_activity, user: target_user)
|
|
||||||
|
|
||||||
{:ok, %{id: report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports")
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
[report] = response["reports"]
|
|
||||||
|
|
||||||
assert length(response["reports"]) == 1
|
|
||||||
assert report["id"] == report_id
|
|
||||||
|
|
||||||
assert response["total"] == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns reports with specified state", %{conn: conn} do
|
|
||||||
[reporter, target_user] = insert_pair(:user)
|
|
||||||
activity = insert(:note_activity, user: target_user)
|
|
||||||
|
|
||||||
{:ok, %{id: first_report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, %{id: second_report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I don't like this user"
|
|
||||||
})
|
|
||||||
|
|
||||||
CommonAPI.update_report_state(second_report_id, "closed")
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports", %{
|
|
||||||
"state" => "open"
|
|
||||||
})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
[open_report] = response["reports"]
|
|
||||||
|
|
||||||
assert length(response["reports"]) == 1
|
|
||||||
assert open_report["id"] == first_report_id
|
|
||||||
|
|
||||||
assert response["total"] == 1
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports", %{
|
|
||||||
"state" => "closed"
|
|
||||||
})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
[closed_report] = response["reports"]
|
|
||||||
|
|
||||||
assert length(response["reports"]) == 1
|
|
||||||
assert closed_report["id"] == second_report_id
|
|
||||||
|
|
||||||
assert response["total"] == 1
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/reports", %{
|
|
||||||
"state" => "resolved"
|
|
||||||
})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert Enum.empty?(response["reports"])
|
|
||||||
assert response["total"] == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 403 when requested by a non-admin" do
|
|
||||||
user = insert(:user)
|
|
||||||
token = insert(:oauth_token, user: user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> assign(:token, token)
|
|
||||||
|> get("/api/pleroma/admin/reports")
|
|
||||||
|
|
||||||
assert json_response(conn, :forbidden) ==
|
|
||||||
%{"error" => "User is not an admin or OAuth admin scope is not granted."}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 403 when requested by anonymous" do
|
|
||||||
conn = get(build_conn(), "/api/pleroma/admin/reports")
|
|
||||||
|
|
||||||
assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/restart" do
|
describe "GET /api/pleroma/admin/restart" do
|
||||||
setup do: clear_config(:configurable_from_database, true)
|
setup do: clear_config(:configurable_from_database, true)
|
||||||
|
|
||||||
|
@ -2251,65 +1744,6 @@ test "it resend emails for two users", %{conn: conn, admin: admin} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /reports/:id/notes" do
|
|
||||||
setup %{conn: conn, admin: admin} do
|
|
||||||
[reporter, target_user] = insert_pair(:user)
|
|
||||||
activity = insert(:note_activity, user: target_user)
|
|
||||||
|
|
||||||
{:ok, %{id: report_id}} =
|
|
||||||
CommonAPI.report(reporter, %{
|
|
||||||
account_id: target_user.id,
|
|
||||||
comment: "I feel offended",
|
|
||||||
status_ids: [activity.id]
|
|
||||||
})
|
|
||||||
|
|
||||||
post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
|
|
||||||
content: "this is disgusting!"
|
|
||||||
})
|
|
||||||
|
|
||||||
post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
|
|
||||||
content: "this is disgusting2!"
|
|
||||||
})
|
|
||||||
|
|
||||||
%{
|
|
||||||
admin_id: admin.id,
|
|
||||||
report_id: report_id
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
|
|
||||||
[note, _] = Repo.all(ReportNote)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
activity_id: ^report_id,
|
|
||||||
content: "this is disgusting!",
|
|
||||||
user_id: ^admin_id
|
|
||||||
} = note
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns reports with notes", %{conn: conn, admin: admin} do
|
|
||||||
conn = get(conn, "/api/pleroma/admin/reports")
|
|
||||||
|
|
||||||
response = json_response(conn, 200)
|
|
||||||
notes = hd(response["reports"])["notes"]
|
|
||||||
[note, _] = notes
|
|
||||||
|
|
||||||
assert note["user"]["nickname"] == admin.nickname
|
|
||||||
assert note["content"] == "this is disgusting!"
|
|
||||||
assert note["created_at"]
|
|
||||||
assert response["total"] == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it deletes the note", %{conn: conn, report_id: report_id} do
|
|
||||||
assert ReportNote |> Repo.all() |> length() == 2
|
|
||||||
|
|
||||||
[note, _] = Repo.all(ReportNote)
|
|
||||||
|
|
||||||
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
|
|
||||||
|
|
||||||
assert ReportNote |> Repo.all() |> length() == 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "/api/pleroma/admin/stats" do
|
describe "/api/pleroma/admin/stats" do
|
||||||
test "status visibility count", %{conn: conn} do
|
test "status visibility count", %{conn: conn} do
|
||||||
|
@ -2329,191 +1763,6 @@ test "status visibility count", %{conn: conn} do
|
||||||
response["status_visibility"]
|
response["status_visibility"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/oauth_app" do
|
|
||||||
test "errors", %{conn: conn} do
|
|
||||||
response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
|
|
||||||
|
|
||||||
assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "success", %{conn: conn} do
|
|
||||||
base_url = Web.base_url()
|
|
||||||
app_name = "Trusted app"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/admin/oauth_app", %{
|
|
||||||
name: app_name,
|
|
||||||
redirect_uris: base_url
|
|
||||||
})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => _,
|
|
||||||
"client_secret" => _,
|
|
||||||
"name" => ^app_name,
|
|
||||||
"redirect_uri" => ^base_url,
|
|
||||||
"trusted" => false
|
|
||||||
} = response
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with trusted", %{conn: conn} do
|
|
||||||
base_url = Web.base_url()
|
|
||||||
app_name = "Trusted app"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/admin/oauth_app", %{
|
|
||||||
name: app_name,
|
|
||||||
redirect_uris: base_url,
|
|
||||||
trusted: true
|
|
||||||
})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => _,
|
|
||||||
"client_secret" => _,
|
|
||||||
"name" => ^app_name,
|
|
||||||
"redirect_uri" => ^base_url,
|
|
||||||
"trusted" => true
|
|
||||||
} = response
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/oauth_app" do
|
|
||||||
setup do
|
|
||||||
app = insert(:oauth_app)
|
|
||||||
{:ok, app: app}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "list", %{conn: conn} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/oauth_app")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{"apps" => apps, "count" => count, "page_size" => _} = response
|
|
||||||
|
|
||||||
assert length(apps) == count
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with page size", %{conn: conn} do
|
|
||||||
insert(:oauth_app)
|
|
||||||
page_size = 1
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
|
|
||||||
|
|
||||||
assert length(apps) == page_size
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search by client name", %{conn: conn, app: app} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
|
||||||
|
|
||||||
assert returned["client_id"] == app.client_id
|
|
||||||
assert returned["name"] == app.client_name
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search by client id", %{conn: conn, app: app} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
|
||||||
|
|
||||||
assert returned["client_id"] == app.client_id
|
|
||||||
assert returned["name"] == app.client_name
|
|
||||||
end
|
|
||||||
|
|
||||||
test "only trusted", %{conn: conn} do
|
|
||||||
app = insert(:oauth_app, trusted: true)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/oauth_app", %{trusted: true})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
|
||||||
|
|
||||||
assert returned["client_id"] == app.client_id
|
|
||||||
assert returned["name"] == app.client_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "DELETE /api/pleroma/admin/oauth_app/:id" do
|
|
||||||
test "with id", %{conn: conn} do
|
|
||||||
app = insert(:oauth_app)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
|
|
||||||
|> json_response(:no_content)
|
|
||||||
|
|
||||||
assert response == ""
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with non existance id", %{conn: conn} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> delete("/api/pleroma/admin/oauth_app/0")
|
|
||||||
|> json_response(:bad_request)
|
|
||||||
|
|
||||||
assert response == ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PATCH /api/pleroma/admin/oauth_app/:id" do
|
|
||||||
test "with id", %{conn: conn} do
|
|
||||||
app = insert(:oauth_app)
|
|
||||||
|
|
||||||
name = "another name"
|
|
||||||
url = "https://example.com"
|
|
||||||
scopes = ["admin"]
|
|
||||||
id = app.id
|
|
||||||
website = "http://website.com"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
|
|
||||||
name: name,
|
|
||||||
trusted: true,
|
|
||||||
redirect_uris: url,
|
|
||||||
scopes: scopes,
|
|
||||||
website: website
|
|
||||||
})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => _,
|
|
||||||
"client_secret" => _,
|
|
||||||
"id" => ^id,
|
|
||||||
"name" => ^name,
|
|
||||||
"redirect_uri" => ^url,
|
|
||||||
"trusted" => true,
|
|
||||||
"website" => ^website
|
|
||||||
} = response
|
|
||||||
end
|
|
||||||
|
|
||||||
test "without id", %{conn: conn} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> patch("/api/pleroma/admin/oauth_app/0")
|
|
||||||
|> json_response(:bad_request)
|
|
||||||
|
|
||||||
assert response == ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Needed for testing
|
# Needed for testing
|
||||||
|
|
281
test/web/admin_api/controllers/invite_controller_test.exs
Normal file
281
test/web/admin_api/controllers/invite_controller_test.exs
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.InviteControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/email_invite, with valid config" do
|
||||||
|
setup do: clear_config([:instance, :registrations_open], false)
|
||||||
|
setup do: clear_config([:instance, :invites_enabled], true)
|
||||||
|
|
||||||
|
test "sends invitation and returns 204", %{admin: admin, conn: conn} do
|
||||||
|
recipient_email = "foo@bar.com"
|
||||||
|
recipient_name = "J. D."
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: recipient_email,
|
||||||
|
name: recipient_name
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :no_content)
|
||||||
|
|
||||||
|
token_record = List.last(Repo.all(Pleroma.UserInviteToken))
|
||||||
|
assert token_record
|
||||||
|
refute token_record.used
|
||||||
|
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
email =
|
||||||
|
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
admin,
|
||||||
|
token_record,
|
||||||
|
recipient_email,
|
||||||
|
recipient_name
|
||||||
|
)
|
||||||
|
|
||||||
|
Swoosh.TestAssertions.assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: {recipient_name, recipient_email},
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 403 if requested by a non-admin" do
|
||||||
|
non_admin_user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: non_admin_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, non_admin_user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "email with +", %{conn: conn, admin: admin} do
|
||||||
|
recipient_email = "foo+bar@baz.com"
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
token_record =
|
||||||
|
Pleroma.UserInviteToken
|
||||||
|
|> Repo.all()
|
||||||
|
|> List.last()
|
||||||
|
|
||||||
|
assert token_record
|
||||||
|
refute token_record.used
|
||||||
|
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
email =
|
||||||
|
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
admin,
|
||||||
|
token_record,
|
||||||
|
recipient_email
|
||||||
|
)
|
||||||
|
|
||||||
|
Swoosh.TestAssertions.assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: recipient_email,
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
|
||||||
|
setup do: clear_config([:instance, :registrations_open])
|
||||||
|
setup do: clear_config([:instance, :invites_enabled])
|
||||||
|
|
||||||
|
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
|
||||||
|
Config.put([:instance, :registrations_open], false)
|
||||||
|
Config.put([:instance, :invites_enabled], false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
|
"To send invites you need to set the `invites_enabled` option to true."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
||||||
|
Config.put([:instance, :registrations_open], true)
|
||||||
|
Config.put([:instance, :invites_enabled], true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
|
"To send invites you need to set the `registrations_open` option to false."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/invite_token" do
|
||||||
|
test "without options", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token")
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "one_time"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with expires_at", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{
|
||||||
|
"expires_at" => Date.to_string(Date.utc_today())
|
||||||
|
})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "date_limited"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max_use", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max use and expires_at", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{
|
||||||
|
"max_use" => 150,
|
||||||
|
"expires_at" => Date.to_string(Date.utc_today())
|
||||||
|
})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable_date_limited"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/users/invites" do
|
||||||
|
test "no invites", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/invites")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{"invites" => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invite", %{conn: conn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/invites")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{
|
||||||
|
"invites" => [
|
||||||
|
%{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => false,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/revoke_invite" do
|
||||||
|
test "with token", %{conn: conn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => true,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid token", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
220
test/web/admin_api/controllers/oauth_app_controller_test.exs
Normal file
220
test/web/admin_api/controllers/oauth_app_controller_test.exs
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
# 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.OAuthAppControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/oauth_app" do
|
||||||
|
test "errors", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{})
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"error" => "Missing field: name. Missing field: redirect_uris."
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "success", %{conn: conn} do
|
||||||
|
base_url = Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => false
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with trusted", %{conn: conn} do
|
||||||
|
base_url = Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url,
|
||||||
|
trusted: true
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => true
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/oauth_app" do
|
||||||
|
setup do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
{:ok, app: app}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => count, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert length(apps) == count
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with page size", %{conn: conn} do
|
||||||
|
insert(:oauth_app)
|
||||||
|
page_size = 1
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
|
||||||
|
|
||||||
|
assert length(apps) == page_size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client name", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client id", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only trusted", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app, trusted: true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?trusted=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with non existance id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
name = "another name"
|
||||||
|
url = "https://example.com"
|
||||||
|
scopes = ["admin"]
|
||||||
|
id = app.id
|
||||||
|
website = "http://website.com"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/#{id}", %{
|
||||||
|
name: name,
|
||||||
|
trusted: true,
|
||||||
|
redirect_uris: url,
|
||||||
|
scopes: scopes,
|
||||||
|
website: website
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"id" => ^id,
|
||||||
|
"name" => ^name,
|
||||||
|
"redirect_uri" => ^url,
|
||||||
|
"trusted" => true,
|
||||||
|
"website" => ^website
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
374
test/web/admin_api/controllers/report_controller_test.exs
Normal file
374
test/web/admin_api/controllers/report_controller_test.exs
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
# 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.ReportControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.ReportNote
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/reports/:id" do
|
||||||
|
test "returns report by its id", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports/#{report_id}")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["id"] == report_id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/reports/test")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /api/pleroma/admin/reports" do
|
||||||
|
setup do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %{id: second_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel very offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: report_id,
|
||||||
|
second_report_id: second_report_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
|
||||||
|
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
||||||
|
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:token, read_token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [%{"state" => "resolved", "id" => id}]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(403)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"error" => "Insufficient permissions: admin:write:reports."
|
||||||
|
}
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:token, write_token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [%{"state" => "resolved", "id" => id}]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "resolved", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
assert activity.data["state"] == "resolved"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "closes report", %{conn: conn, id: id, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "closed", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
assert activity.data["state"] == "closed"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'closed' state"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 400 when state is unknown", %{conn: conn, id: id} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "test", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert "Unsupported state" =
|
||||||
|
hd(json_response_and_validate_schema(conn, :bad_request))["error"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when report is not exist", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "closed", "id" => "test"}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates state of multiple reports", %{
|
||||||
|
conn: conn,
|
||||||
|
id: id,
|
||||||
|
admin: admin,
|
||||||
|
second_report_id: second_report_id
|
||||||
|
} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "resolved", "id" => id},
|
||||||
|
%{"state" => "closed", "id" => second_report_id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
second_activity = Activity.get_by_id(second_report_id)
|
||||||
|
assert activity.data["state"] == "resolved"
|
||||||
|
assert second_activity.data["state"] == "closed"
|
||||||
|
|
||||||
|
[first_log_entry, second_log_entry] = Repo.all(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(first_log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(second_log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/reports" do
|
||||||
|
test "returns empty response when no reports created", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert Enum.empty?(response["reports"])
|
||||||
|
assert response["total"] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns reports", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
[report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert report["id"] == report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns reports with specified state", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: first_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %{id: second_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I don't like this user"
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.update_report_state(second_report_id, "closed")
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=open")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert [open_report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert open_report["id"] == first_report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=closed")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert [closed_report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert closed_report["id"] == second_report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
|
||||||
|
assert %{"total" => 0, "reports" => []} ==
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=resolved", %{
|
||||||
|
"" => ""
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 when requested by a non-admin" do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden) ==
|
||||||
|
%{"error" => "User is not an admin or OAuth admin scope is not granted."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 when requested by anonymous" do
|
||||||
|
conn = get(build_conn(), "/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden) == %{
|
||||||
|
"error" => "Invalid credentials."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/reports/:id/notes" do
|
||||||
|
setup %{conn: conn, admin: admin} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||||
|
content: "this is disgusting!"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||||
|
content: "this is disgusting2!"
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
admin_id: admin.id,
|
||||||
|
report_id: report_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
|
||||||
|
assert [note, _] = Repo.all(ReportNote)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
activity_id: ^report_id,
|
||||||
|
content: "this is disgusting!",
|
||||||
|
user_id: ^admin_id
|
||||||
|
} = note
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns reports with notes", %{conn: conn, admin: admin} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
notes = hd(response["reports"])["notes"]
|
||||||
|
[note, _] = notes
|
||||||
|
|
||||||
|
assert note["user"]["nickname"] == admin.nickname
|
||||||
|
assert note["content"] == "this is disgusting!"
|
||||||
|
assert note["created_at"]
|
||||||
|
assert response["total"] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it deletes the note", %{conn: conn, report_id: report_id} do
|
||||||
|
assert ReportNote |> Repo.all() |> length() == 2
|
||||||
|
assert [note, _] = Repo.all(ReportNote)
|
||||||
|
|
||||||
|
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
|
||||||
|
|
||||||
|
assert ReportNote |> Repo.all() |> length() == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,6 +42,14 @@ test "shows activity", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert response["id"] == activity.id
|
assert response["id"] == activity.id
|
||||||
|
|
||||||
|
account = response["account"]
|
||||||
|
actor = User.get_by_ap_id(activity.actor)
|
||||||
|
|
||||||
|
assert account["id"] == actor.id
|
||||||
|
assert account["nickname"] == actor.nickname
|
||||||
|
assert account["deactivated"] == actor.deactivated
|
||||||
|
assert account["confirmation_pending"] == actor.confirmation_pending
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||||
|
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup do: clear_config([:instance, :max_account_fields])
|
setup do: clear_config([:instance, :max_account_fields])
|
||||||
|
@ -52,6 +53,10 @@ test "sets user settings in a generic way", %{conn: conn} do
|
||||||
|
|
||||||
user = Repo.get(User, user_data["id"])
|
user = Repo.get(User, user_data["id"])
|
||||||
|
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
|
with_mock Pleroma.Web.Federator,
|
||||||
|
publish: fn _activity -> :ok end do
|
||||||
res_conn =
|
res_conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|
@ -70,6 +75,9 @@ test "sets user settings in a generic way", %{conn: conn} do
|
||||||
"pleroma_fe" => %{"theme" => "bla"},
|
"pleroma_fe" => %{"theme" => "bla"},
|
||||||
"masto_fe" => %{"theme" => "blub"}
|
"masto_fe" => %{"theme" => "blub"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_called(Pleroma.Web.Federator.publish(:_))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates the user's bio", %{conn: conn} do
|
test "updates the user's bio", %{conn: conn} do
|
||||||
|
|
|
@ -12,19 +12,24 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
|
|
||||||
setup do: oauth_access(["read:statuses"])
|
setup do: oauth_access(["read:statuses"])
|
||||||
|
|
||||||
test "returns a list of conversations", %{user: user_one, conn: conn} do
|
describe "returns a list of conversations" do
|
||||||
|
setup(%{user: user_one, conn: conn}) do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
user_three = insert(:user)
|
user_three = insert(:user)
|
||||||
|
|
||||||
{:ok, user_two} = User.follow(user_two, user_one)
|
{:ok, user_two} = User.follow(user_two, user_one)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
{:ok, %{user: user_one, user_two: user_two, user_three: user_three, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
{:ok, direct} =
|
test "returns correct conversations", %{
|
||||||
CommonAPI.post(user_one, %{
|
user: user_one,
|
||||||
status: "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
|
user_two: user_two,
|
||||||
visibility: "direct"
|
user_three: user_three,
|
||||||
})
|
conn: conn
|
||||||
|
} do
|
||||||
|
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
||||||
|
{:ok, direct} = create_direct_message(user_one, [user_two, user_three])
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
||||||
|
|
||||||
|
@ -57,39 +62,38 @@ test "returns a list of conversations", %{user: user_one, conn: conn} do
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "observes limit params", %{
|
||||||
|
user: user_one,
|
||||||
|
user_two: user_two,
|
||||||
|
user_three: user_three,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
{:ok, _} = create_direct_message(user_one, [user_two, user_three])
|
||||||
|
{:ok, _} = create_direct_message(user_two, [user_one, user_three])
|
||||||
|
{:ok, _} = create_direct_message(user_three, [user_two, user_one])
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/conversations?limit=1")
|
||||||
|
|
||||||
|
assert response = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
|
||||||
|
assert Enum.count(response) == 1
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/conversations?limit=2")
|
||||||
|
|
||||||
|
assert response = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
|
||||||
|
assert Enum.count(response) == 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "filters conversations by recipients", %{user: user_one, conn: conn} do
|
test "filters conversations by recipients", %{user: user_one, conn: conn} do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
user_three = insert(:user)
|
user_three = insert(:user)
|
||||||
|
{:ok, direct1} = create_direct_message(user_one, [user_two])
|
||||||
{:ok, direct1} =
|
{:ok, _direct2} = create_direct_message(user_one, [user_three])
|
||||||
CommonAPI.post(user_one, %{
|
{:ok, direct3} = create_direct_message(user_one, [user_two, user_three])
|
||||||
status: "Hi @#{user_two.nickname}!",
|
{:ok, _direct4} = create_direct_message(user_two, [user_three])
|
||||||
visibility: "direct"
|
{:ok, direct5} = create_direct_message(user_two, [user_one])
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, _direct2} =
|
|
||||||
CommonAPI.post(user_one, %{
|
|
||||||
status: "Hi @#{user_three.nickname}!",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, direct3} =
|
|
||||||
CommonAPI.post(user_one, %{
|
|
||||||
status: "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, _direct4} =
|
|
||||||
CommonAPI.post(user_two, %{
|
|
||||||
status: "Hi @#{user_three.nickname}!",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, direct5} =
|
|
||||||
CommonAPI.post(user_two, %{
|
|
||||||
status: "Hi @#{user_one.nickname}!",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert [conversation1, conversation2] =
|
assert [conversation1, conversation2] =
|
||||||
conn
|
conn
|
||||||
|
@ -109,12 +113,7 @@ test "filters conversations by recipients", %{user: user_one, conn: conn} do
|
||||||
|
|
||||||
test "updates the last_status on reply", %{user: user_one, conn: conn} do
|
test "updates the last_status on reply", %{user: user_one, conn: conn} do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
|
{:ok, direct} = create_direct_message(user_one, [user_two])
|
||||||
{:ok, direct} =
|
|
||||||
CommonAPI.post(user_one, %{
|
|
||||||
status: "Hi @#{user_two.nickname}",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, direct_reply} =
|
{:ok, direct_reply} =
|
||||||
CommonAPI.post(user_two, %{
|
CommonAPI.post(user_two, %{
|
||||||
|
@ -133,12 +132,7 @@ test "updates the last_status on reply", %{user: user_one, conn: conn} do
|
||||||
|
|
||||||
test "the user marks a conversation as read", %{user: user_one, conn: conn} do
|
test "the user marks a conversation as read", %{user: user_one, conn: conn} do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
|
{:ok, direct} = create_direct_message(user_one, [user_two])
|
||||||
{:ok, direct} =
|
|
||||||
CommonAPI.post(user_one, %{
|
|
||||||
status: "Hi @#{user_two.nickname}",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
||||||
|
@ -194,15 +188,22 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do
|
||||||
|
|
||||||
test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do
|
test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
|
{:ok, direct} = create_direct_message(user_one, [user_two])
|
||||||
{:ok, direct} =
|
|
||||||
CommonAPI.post(user_one, %{
|
|
||||||
status: "Hi @#{user_two.nickname}!",
|
|
||||||
visibility: "direct"
|
|
||||||
})
|
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context")
|
res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context")
|
||||||
|
|
||||||
assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
|
assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp create_direct_message(sender, recips) do
|
||||||
|
hellos =
|
||||||
|
recips
|
||||||
|
|> Enum.map(fn s -> "@#{s.nickname}" end)
|
||||||
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
CommonAPI.post(sender, %{
|
||||||
|
status: "Hi #{hellos}!",
|
||||||
|
visibility: "direct"
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -71,10 +71,48 @@ test "search", %{conn: conn} do
|
||||||
get(conn, "/api/v2/search?q=天子")
|
get(conn, "/api/v2/search?q=天子")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"}
|
||||||
|
]
|
||||||
|
|
||||||
[status] = results["statuses"]
|
[status] = results["statuses"]
|
||||||
assert status["id"] == to_string(activity.id)
|
assert status["id"] == to_string(activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "constructs hashtags from search query", %{conn: conn} do
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"},
|
||||||
|
%{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"}
|
||||||
|
]
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "john", "url" => "#{Web.base_url()}/tag/john"},
|
||||||
|
%{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"},
|
||||||
|
%{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"}
|
||||||
|
]
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"},
|
||||||
|
%{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"},
|
||||||
|
%{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
test "excludes a blocked users from search results", %{conn: conn} do
|
test "excludes a blocked users from search results", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
||||||
|
@ -179,7 +217,7 @@ test "search", %{conn: conn} do
|
||||||
[account | _] = results["accounts"]
|
[account | _] = results["accounts"]
|
||||||
assert account["id"] == to_string(user_three.id)
|
assert account["id"] == to_string(user_three.id)
|
||||||
|
|
||||||
assert results["hashtags"] == []
|
assert results["hashtags"] == ["2hu"]
|
||||||
|
|
||||||
[status] = results["statuses"]
|
[status] = results["statuses"]
|
||||||
assert status["id"] == to_string(activity.id)
|
assert status["id"] == to_string(activity.id)
|
||||||
|
|
|
@ -60,9 +60,9 @@ test "the home timeline when the direct messages are excluded", %{user: user, co
|
||||||
describe "public" do
|
describe "public" do
|
||||||
@tag capture_log: true
|
@tag capture_log: true
|
||||||
test "the public timeline", %{conn: conn} do
|
test "the public timeline", %{conn: conn} do
|
||||||
following = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(following, %{status: "test"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
|
||||||
|
|
||||||
_activity = insert(:note_activity, local: false)
|
_activity = insert(:note_activity, local: false)
|
||||||
|
|
||||||
|
@ -77,6 +77,13 @@ test "the public timeline", %{conn: conn} do
|
||||||
conn = get(build_conn(), "/api/v1/timelines/public?local=1")
|
conn = get(build_conn(), "/api/v1/timelines/public?local=1")
|
||||||
|
|
||||||
assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
|
assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
|
||||||
|
|
||||||
|
# does not contain repeats
|
||||||
|
{:ok, _} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
|
conn = get(build_conn(), "/api/v1/timelines/public?local=true")
|
||||||
|
|
||||||
|
assert [_] = json_response_and_validate_schema(conn, :ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the public timeline includes only public statuses for an authenticated user" do
|
test "the public timeline includes only public statuses for an authenticated user" do
|
||||||
|
@ -90,6 +97,49 @@ test "the public timeline includes only public statuses for an authenticated use
|
||||||
res_conn = get(conn, "/api/v1/timelines/public")
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't return replies if follower is posting with blocked user" do
|
||||||
|
%{conn: conn, user: blocker} = oauth_access(["read:statuses"])
|
||||||
|
[blockee, friend] = insert_list(2, :user)
|
||||||
|
{:ok, blocker} = User.follow(blocker, friend)
|
||||||
|
{:ok, _} = User.block(blocker, blockee)
|
||||||
|
|
||||||
|
conn = assign(conn, :user, blocker)
|
||||||
|
|
||||||
|
{:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
|
||||||
|
|
||||||
|
{:ok, reply_from_blockee} =
|
||||||
|
CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
|
||||||
|
|
||||||
|
{:ok, _reply_from_friend} =
|
||||||
|
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
|
[%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't return replies if follow is posting with users from blocked domain" do
|
||||||
|
%{conn: conn, user: blocker} = oauth_access(["read:statuses"])
|
||||||
|
friend = insert(:user)
|
||||||
|
blockee = insert(:user, ap_id: "https://example.com/users/blocked")
|
||||||
|
{:ok, blocker} = User.follow(blocker, friend)
|
||||||
|
{:ok, blocker} = User.block_domain(blocker, "example.com")
|
||||||
|
|
||||||
|
conn = assign(conn, :user, blocker)
|
||||||
|
|
||||||
|
{:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
|
||||||
|
|
||||||
|
{:ok, reply_from_blockee} =
|
||||||
|
CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
|
||||||
|
|
||||||
|
{:ok, _reply_from_friend} =
|
||||||
|
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
|
|
||||||
|
activities = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
[%{"id" => ^activity_id}] = activities
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_activities do
|
defp local_and_remote_activities do
|
||||||
|
|
|
@ -54,10 +54,10 @@ test "Represent a user account" do
|
||||||
header_static: "http://localhost:4001/images/banner.png",
|
header_static: "http://localhost:4001/images/banner.png",
|
||||||
emojis: [
|
emojis: [
|
||||||
%{
|
%{
|
||||||
"static_url" => "/file.png",
|
static_url: "/file.png",
|
||||||
"url" => "/file.png",
|
url: "/file.png",
|
||||||
"shortcode" => "karjalanpiirakka",
|
shortcode: "karjalanpiirakka",
|
||||||
"visible_in_picker" => false
|
visible_in_picker: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
fields: [],
|
fields: [],
|
||||||
|
@ -491,4 +491,31 @@ test "shows non-zero when historical unapproved requests are present" do
|
||||||
AccountView.render("show.json", %{user: user, for: user})
|
AccountView.render("show.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "uses mediaproxy urls when it's enabled" do
|
||||||
|
clear_config([:media_proxy, :enabled], true)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]},
|
||||||
|
banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]},
|
||||||
|
emoji: %{"joker_smile" => "https://evil.website/society.png"}
|
||||||
|
)
|
||||||
|
|
||||||
|
AccountView.render("show.json", %{user: user})
|
||||||
|
|> Enum.all?(fn
|
||||||
|
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
|
||||||
|
String.starts_with?(url, Pleroma.Web.base_url())
|
||||||
|
|
||||||
|
{:emojis, emojis} ->
|
||||||
|
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
|
||||||
|
String.starts_with?(url, Pleroma.Web.base_url()) &&
|
||||||
|
String.starts_with?(static_url, Pleroma.Web.base_url())
|
||||||
|
end)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
true
|
||||||
|
end)
|
||||||
|
|> assert()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -124,15 +124,7 @@ test "encoded url are tried to match for proxy as `conn.request_path` encodes th
|
||||||
end
|
end
|
||||||
|
|
||||||
test "uses the configured base_url" do
|
test "uses the configured base_url" do
|
||||||
base_url = Pleroma.Config.get([:media_proxy, :base_url])
|
clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
|
||||||
|
|
||||||
if base_url do
|
|
||||||
on_exit(fn ->
|
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], base_url)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
|
||||||
|
|
||||||
url = "https://pleroma.soykaf.com/static/logo.png"
|
url = "https://pleroma.soykaf.com/static/logo.png"
|
||||||
encoded = url(url)
|
encoded = url(url)
|
||||||
|
@ -213,8 +205,8 @@ test "mediaproxy whitelist" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not change whitelisted urls" do
|
test "does not change whitelisted urls" do
|
||||||
Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"])
|
clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"])
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
|
||||||
|
|
||||||
media_url = "https://mycdn.akamai.com"
|
media_url = "https://mycdn.akamai.com"
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,25 @@ test "it streams boosts of the user in the 'user' stream", %{user: user} do
|
||||||
refute Streamer.filtered_by_user?(user, announce)
|
refute Streamer.filtered_by_user?(user, announce)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do
|
||||||
|
Streamer.get_topic_and_add_socket("user", user)
|
||||||
|
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"])
|
||||||
|
|> Map.put("actor", user.ap_id)
|
||||||
|
|
||||||
|
{:ok, %Pleroma.Activity{data: _data, local: false} = announce} =
|
||||||
|
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
|
||||||
|
refute Streamer.filtered_by_user?(user, announce)
|
||||||
|
end
|
||||||
|
|
||||||
test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do
|
test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do
|
||||||
Streamer.get_topic_and_add_socket("user", user)
|
Streamer.get_topic_and_add_socket("user", user)
|
||||||
Streamer.stream("user", notify)
|
Streamer.stream("user", notify)
|
||||||
|
|
Loading…
Reference in a new issue