forked from YokaiRick/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
f1c77f9682
31 changed files with 385 additions and 214 deletions
|
@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
- Refreshing poll results for remote polls
|
- Refreshing poll results for remote polls
|
||||||
|
- Job queue stats to the healthcheck page
|
||||||
- Admin API: Add ability to require password reset
|
- Admin API: Add ability to require password reset
|
||||||
- Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
|
- Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
|
||||||
- Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items
|
- Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items
|
||||||
|
@ -23,6 +24,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
|
- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
|
||||||
|
- Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
|
||||||
|
- Added `:instance, extended_nickname_format` setting to the default config
|
||||||
|
|
||||||
## [1.1.0] - 2019-??-??
|
## [1.1.0] - 2019-??-??
|
||||||
### Security
|
### Security
|
||||||
|
@ -74,6 +77,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub: Deactivated user deletion
|
- ActivityPub: Deactivated user deletion
|
||||||
- ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user
|
- ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user
|
||||||
- MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
|
- MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
|
||||||
|
- Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
|
- Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
|
||||||
|
|
|
@ -279,7 +279,8 @@
|
||||||
max_remote_account_fields: 20,
|
max_remote_account_fields: 20,
|
||||||
account_field_name_length: 512,
|
account_field_name_length: 512,
|
||||||
account_field_value_length: 2048,
|
account_field_value_length: 2048,
|
||||||
external_user_synchronization: true
|
external_user_synchronization: true,
|
||||||
|
extended_nickname_format: false
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
|
|
@ -317,7 +317,8 @@ See [Admin-API](admin_api.md)
|
||||||
"active": 0, # active processes
|
"active": 0, # active processes
|
||||||
"idle": 0, # idle processes
|
"idle": 0, # idle processes
|
||||||
"memory_used": 0.00, # Memory used
|
"memory_used": 0.00, # Memory used
|
||||||
"healthy": true # Instance state
|
"healthy": true, # Instance state
|
||||||
|
"job_queue_stats": {} # Job queue stats
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -391,7 +392,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
||||||
### Update a file in a custom emoji pack
|
### Update a file in a custom emoji pack
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params:
|
||||||
* if the `action` is `add`, adds an emoji named `shortcode` to the pack `pack_name`,
|
* if the `action` is `add`, adds an emoji named `shortcode` to the pack `pack_name`,
|
||||||
that means that the emoji file needs to be uploaded with the request
|
that means that the emoji file needs to be uploaded with the request
|
||||||
(thus requiring it to be a multipart request) and be named `file`.
|
(thus requiring it to be a multipart request) and be named `file`.
|
||||||
|
@ -408,7 +409,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
||||||
### Updates (replaces) pack metadata
|
### Updates (replaces) pack metadata
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params:
|
||||||
* `new_data`: new metadata to replace the old one
|
* `new_data`: new metadata to replace the old one
|
||||||
* Response: JSON, updated "metadata" section of the pack and 200 status or 400 if there was a
|
* Response: JSON, updated "metadata" section of the pack and 200 status or 400 if there was a
|
||||||
problem with the new metadata (the error is specified in the "error" part of the response JSON)
|
problem with the new metadata (the error is specified in the "error" part of the response JSON)
|
||||||
|
@ -417,7 +418,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
||||||
### Requests the instance to download the pack from another instance
|
### Requests the instance to download the pack from another instance
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params:
|
||||||
* `instance_address`: the address of the instance to download from
|
* `instance_address`: the address of the instance to download from
|
||||||
* `pack_name`: the pack to download from that instance
|
* `pack_name`: the pack to download from that instance
|
||||||
* Response: JSON, "ok" and 200 status if the pack was downloaded, or 500 if there were
|
* Response: JSON, "ok" and 200 status if the pack was downloaded, or 500 if there were
|
||||||
|
|
|
@ -42,6 +42,7 @@ def start(_type, _args) do
|
||||||
hackney_pool_children() ++
|
hackney_pool_children() ++
|
||||||
[
|
[
|
||||||
Pleroma.Stats,
|
Pleroma.Stats,
|
||||||
|
Pleroma.JobQueueMonitor,
|
||||||
{Oban, Pleroma.Config.get(Oban)}
|
{Oban, Pleroma.Config.get(Oban)}
|
||||||
] ++
|
] ++
|
||||||
task_children(@env) ++
|
task_children(@env) ++
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Healthcheck do
|
||||||
active: 0,
|
active: 0,
|
||||||
idle: 0,
|
idle: 0,
|
||||||
memory_used: 0,
|
memory_used: 0,
|
||||||
|
job_queue_stats: nil,
|
||||||
healthy: true
|
healthy: true
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
|
@ -21,6 +22,7 @@ defmodule Pleroma.Healthcheck do
|
||||||
active: non_neg_integer(),
|
active: non_neg_integer(),
|
||||||
idle: non_neg_integer(),
|
idle: non_neg_integer(),
|
||||||
memory_used: number(),
|
memory_used: number(),
|
||||||
|
job_queue_stats: map(),
|
||||||
healthy: boolean()
|
healthy: boolean()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ def system_info do
|
||||||
memory_used: Float.round(:erlang.memory(:total) / 1024 / 1024, 2)
|
memory_used: Float.round(:erlang.memory(:total) / 1024 / 1024, 2)
|
||||||
}
|
}
|
||||||
|> assign_db_info()
|
|> assign_db_info()
|
||||||
|
|> assign_job_queue_stats()
|
||||||
|> check_health()
|
|> check_health()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,6 +58,11 @@ defp assign_db_info(healthcheck) do
|
||||||
Map.merge(healthcheck, db_info)
|
Map.merge(healthcheck, db_info)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp assign_job_queue_stats(healthcheck) do
|
||||||
|
stats = Pleroma.JobQueueMonitor.stats()
|
||||||
|
Map.put(healthcheck, :job_queue_stats, stats)
|
||||||
|
end
|
||||||
|
|
||||||
@spec check_health(Healthcheck.t()) :: Healthcheck.t()
|
@spec check_health(Healthcheck.t()) :: Healthcheck.t()
|
||||||
def check_health(%{pool_size: pool_size, active: active} = check)
|
def check_health(%{pool_size: pool_size, active: active} = check)
|
||||||
when active >= pool_size do
|
when active >= pool_size do
|
||||||
|
|
78
lib/pleroma/job_queue_monitor.ex
Normal file
78
lib/pleroma/job_queue_monitor.ex
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.JobQueueMonitor do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
@initial_state %{workers: %{}, queues: %{}, processed_jobs: 0}
|
||||||
|
@queue %{processed_jobs: 0, success: 0, failure: 0}
|
||||||
|
@operation %{processed_jobs: 0, success: 0, failure: 0}
|
||||||
|
|
||||||
|
def start_link(_) do
|
||||||
|
GenServer.start_link(__MODULE__, @initial_state, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(state) do
|
||||||
|
:telemetry.attach("oban-monitor-failure", [:oban, :failure], &handle_event/4, nil)
|
||||||
|
:telemetry.attach("oban-monitor-success", [:oban, :success], &handle_event/4, nil)
|
||||||
|
|
||||||
|
{:ok, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def stats do
|
||||||
|
GenServer.call(__MODULE__, :stats)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event([:oban, status], %{duration: duration}, meta, _) do
|
||||||
|
GenServer.cast(__MODULE__, {:process_event, status, duration, meta})
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call(:stats, _from, state) do
|
||||||
|
{:reply, state, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_cast({:process_event, status, duration, meta}, state) do
|
||||||
|
state =
|
||||||
|
state
|
||||||
|
|> Map.update!(:workers, fn workers ->
|
||||||
|
workers
|
||||||
|
|> Map.put_new(meta.worker, %{})
|
||||||
|
|> Map.update!(meta.worker, &update_worker(&1, status, meta, duration))
|
||||||
|
end)
|
||||||
|
|> Map.update!(:queues, fn workers ->
|
||||||
|
workers
|
||||||
|
|> Map.put_new(meta.queue, @queue)
|
||||||
|
|> Map.update!(meta.queue, &update_queue(&1, status, meta, duration))
|
||||||
|
end)
|
||||||
|
|> Map.update!(:processed_jobs, &(&1 + 1))
|
||||||
|
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_worker(worker, status, meta, duration) do
|
||||||
|
worker
|
||||||
|
|> Map.put_new(meta.args["op"], @operation)
|
||||||
|
|> Map.update!(meta.args["op"], &update_op(&1, status, meta, duration))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_op(op, :enqueue, _meta, _duration) do
|
||||||
|
op
|
||||||
|
|> Map.update!(:enqueued, &(&1 + 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_op(op, status, _meta, _duration) do
|
||||||
|
op
|
||||||
|
|> Map.update!(:processed_jobs, &(&1 + 1))
|
||||||
|
|> Map.update!(status, &(&1 + 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_queue(queue, status, _meta, _duration) do
|
||||||
|
queue
|
||||||
|
|> Map.update!(:processed_jobs, &(&1 + 1))
|
||||||
|
|> Map.update!(status, &(&1 + 1))
|
||||||
|
end
|
||||||
|
end
|
|
@ -583,7 +583,7 @@ def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do
|
||||||
is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
|
is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
|
||||||
get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id)
|
get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id)
|
||||||
|
|
||||||
restrict_to_local == false ->
|
restrict_to_local == false or not String.contains?(nickname_or_id, "@") ->
|
||||||
get_cached_by_nickname(nickname_or_id)
|
get_cached_by_nickname(nickname_or_id)
|
||||||
|
|
||||||
restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) ->
|
restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) ->
|
||||||
|
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Workers.BackgroundWorker
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
@ -291,8 +292,8 @@ def reject(%{to: to, actor: actor, object: object} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
# only accept false as false value
|
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
|
activity_id = params[:activity_id]
|
||||||
|
|
||||||
with data <- %{
|
with data <- %{
|
||||||
"to" => to,
|
"to" => to,
|
||||||
|
@ -301,6 +302,7 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"object" => object
|
"object" => object
|
||||||
},
|
},
|
||||||
|
data <- Utils.maybe_put(data, "id", activity_id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
|
|
@ -82,38 +82,6 @@ def track_object_fetch(conn, object_id) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def object_likes(conn, %{"uuid" => uuid, "page" => page}) do
|
|
||||||
with ap_id <- o_status_url(conn, :object, uuid),
|
|
||||||
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
|
|
||||||
{_, true} <- {:public?, Visibility.is_public?(object)},
|
|
||||||
likes <- Utils.get_object_likes(object) do
|
|
||||||
{page, _} = Integer.parse(page)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type("application/activity+json")
|
|
||||||
|> put_view(ObjectView)
|
|
||||||
|> render("likes.json", %{ap_id: ap_id, likes: likes, page: page})
|
|
||||||
else
|
|
||||||
{:public?, false} ->
|
|
||||||
{:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def object_likes(conn, %{"uuid" => uuid}) do
|
|
||||||
with ap_id <- o_status_url(conn, :object, uuid),
|
|
||||||
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
|
|
||||||
{_, true} <- {:public?, Visibility.is_public?(object)},
|
|
||||||
likes <- Utils.get_object_likes(object) do
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type("application/activity+json")
|
|
||||||
|> put_view(ObjectView)
|
|
||||||
|> render("likes.json", %{ap_id: ap_id, likes: likes})
|
|
||||||
else
|
|
||||||
{:public?, false} ->
|
|
||||||
{:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def activity(conn, %{"uuid" => uuid}) do
|
def activity(conn, %{"uuid" => uuid}) do
|
||||||
with ap_id <- o_status_url(conn, :activity, uuid),
|
with ap_id <- o_status_url(conn, :activity, uuid),
|
||||||
%Activity{} = activity <- Activity.normalize(ap_id),
|
%Activity{} = activity <- Activity.normalize(ap_id),
|
||||||
|
|
|
@ -621,7 +621,8 @@ def handle_incoming(
|
||||||
to: data["to"] || [],
|
to: data["to"] || [],
|
||||||
cc: data["cc"] || [],
|
cc: data["cc"] || [],
|
||||||
object: object,
|
object: object,
|
||||||
actor: actor_id
|
actor: actor_id,
|
||||||
|
activity_id: data["id"]
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
|
|
|
@ -251,16 +251,6 @@ def get_existing_like(actor, %{data: %{"id" => id}}) do
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns like activities targeting an object
|
|
||||||
"""
|
|
||||||
def get_object_likes(%{data: %{"id" => id}}) do
|
|
||||||
id
|
|
||||||
|> Activity.Queries.by_object_id()
|
|
||||||
|> Activity.Queries.by_type("Like")
|
|
||||||
|> Repo.all()
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec make_like_data(User.t(), map(), String.t()) :: map()
|
@spec make_like_data(User.t(), map(), String.t()) :: map()
|
||||||
def make_like_data(
|
def make_like_data(
|
||||||
%User{ap_id: ap_id} = actor,
|
%User{ap_id: ap_id} = actor,
|
||||||
|
@ -461,14 +451,16 @@ def make_announce_data(
|
||||||
"""
|
"""
|
||||||
def make_unannounce_data(
|
def make_unannounce_data(
|
||||||
%User{ap_id: ap_id} = user,
|
%User{ap_id: ap_id} = user,
|
||||||
%Activity{data: %{"context" => context}} = activity,
|
%Activity{data: %{"context" => context, "object" => object}} = activity,
|
||||||
activity_id
|
activity_id
|
||||||
) do
|
) do
|
||||||
|
object = Object.normalize(object)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => activity.data,
|
"object" => activity.data,
|
||||||
"to" => [user.follower_address, activity.data["actor"]],
|
"to" => [user.follower_address, object.data["actor"]],
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|
@ -477,14 +469,16 @@ def make_unannounce_data(
|
||||||
|
|
||||||
def make_unlike_data(
|
def make_unlike_data(
|
||||||
%User{ap_id: ap_id} = user,
|
%User{ap_id: ap_id} = user,
|
||||||
%Activity{data: %{"context" => context}} = activity,
|
%Activity{data: %{"context" => context, "object" => object}} = activity,
|
||||||
activity_id
|
activity_id
|
||||||
) do
|
) do
|
||||||
|
object = Object.normalize(object)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => activity.data,
|
"object" => activity.data,
|
||||||
"to" => [user.follower_address, activity.data["actor"]],
|
"to" => [user.follower_address, object.data["actor"]],
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|
@ -745,6 +739,6 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_put(map, _key, nil), do: map
|
def maybe_put(map, _key, nil), do: map
|
||||||
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
def maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,40 +37,4 @@ def render("object.json", %{object: %Activity{} = activity}) do
|
||||||
|
|
||||||
Map.merge(base, additional)
|
Map.merge(base, additional)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("likes.json", %{ap_id: ap_id, likes: likes, page: page}) do
|
|
||||||
collection(likes, "#{ap_id}/likes", page)
|
|
||||||
|> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("likes.json", %{ap_id: ap_id, likes: likes}) do
|
|
||||||
%{
|
|
||||||
"id" => "#{ap_id}/likes",
|
|
||||||
"type" => "OrderedCollection",
|
|
||||||
"totalItems" => length(likes),
|
|
||||||
"first" => collection(likes, "#{ap_id}/likes", 1)
|
|
||||||
}
|
|
||||||
|> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
|
|
||||||
end
|
|
||||||
|
|
||||||
def collection(collection, iri, page) do
|
|
||||||
offset = (page - 1) * 10
|
|
||||||
items = Enum.slice(collection, offset, 10)
|
|
||||||
items = Enum.map(items, fn object -> Transmogrifier.prepare_object(object.data) end)
|
|
||||||
total = length(collection)
|
|
||||||
|
|
||||||
map = %{
|
|
||||||
"id" => "#{iri}?page=#{page}",
|
|
||||||
"type" => "OrderedCollectionPage",
|
|
||||||
"partOf" => iri,
|
|
||||||
"totalItems" => total,
|
|
||||||
"orderedItems" => items
|
|
||||||
}
|
|
||||||
|
|
||||||
if offset + length(items) < total do
|
|
||||||
Map.put(map, "next", "#{iri}?page=#{page + 1}")
|
|
||||||
else
|
|
||||||
map
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
import Pleroma.Web.CommonAPI.Utils
|
import Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
def follow(follower, followed) do
|
def follow(follower, followed) do
|
||||||
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
||||||
|
|
||||||
|
@ -271,7 +273,7 @@ def update(user) do
|
||||||
|
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
local: true,
|
local: true,
|
||||||
to: [user.follower_address],
|
to: [Pleroma.Constants.as_public(), user.follower_address],
|
||||||
cc: [],
|
cc: [],
|
||||||
actor: user.ap_id,
|
actor: user.ap_id,
|
||||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||||
|
|
|
@ -25,40 +25,44 @@ def render("show.json", %{
|
||||||
parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||||
|
|
||||||
response = %{
|
with %{id: _} = account <- AccountView.render("show.json", %{user: actor, for: user}) do
|
||||||
id: to_string(notification.id),
|
response = %{
|
||||||
type: mastodon_type,
|
id: to_string(notification.id),
|
||||||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
type: mastodon_type,
|
||||||
account: AccountView.render("show.json", %{user: actor, for: user}),
|
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
||||||
pleroma: %{
|
account: account,
|
||||||
is_seen: notification.seen
|
pleroma: %{
|
||||||
|
is_seen: notification.seen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case mastodon_type do
|
case mastodon_type do
|
||||||
"mention" ->
|
"mention" ->
|
||||||
response
|
response
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
status: StatusView.render("show.json", %{activity: activity, for: user})
|
||||||
})
|
})
|
||||||
|
|
||||||
"favourite" ->
|
"favourite" ->
|
||||||
response
|
response
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||||
})
|
})
|
||||||
|
|
||||||
"reblog" ->
|
"reblog" ->
|
||||||
response
|
response
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||||
})
|
})
|
||||||
|
|
||||||
"follow" ->
|
"follow" ->
|
||||||
response
|
response
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -460,7 +460,7 @@ defp do_create_authorization(
|
||||||
end
|
end
|
||||||
|
|
||||||
# Special case: Local MastodonFE
|
# Special case: Local MastodonFE
|
||||||
defp redirect_uri(%Plug.Conn{} = conn, "."), do: mastodon_api_url(conn, :login)
|
defp redirect_uri(%Plug.Conn{} = conn, "."), do: auth_url(conn, :login)
|
||||||
|
|
||||||
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
|
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
|
||||||
|
|
||||||
|
|
|
@ -580,7 +580,6 @@ defmodule Pleroma.Web.Router do
|
||||||
pipe_through(:ostatus)
|
pipe_through(:ostatus)
|
||||||
|
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
get("/objects/:uuid/likes", ActivityPubController, :object_likes)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :activitypub_client do
|
pipeline :activitypub_client do
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Streamer.Ping do
|
defmodule Pleroma.Web.Streamer.Ping do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Streamer.State do
|
defmodule Pleroma.Web.Streamer.State do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Streamer.StreamerSocket do
|
defmodule Pleroma.Web.Streamer.StreamerSocket do
|
||||||
defstruct transport_pid: nil, user: nil
|
defstruct transport_pid: nil, user: nil
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Streamer.Supervisor do
|
defmodule Pleroma.Web.Streamer.Supervisor do
|
||||||
use Supervisor
|
use Supervisor
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Streamer.Worker do
|
defmodule Pleroma.Web.Streamer.Worker do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
|
@ -128,11 +132,14 @@ defp should_send?(%User{} = user, %Activity{} = item) do
|
||||||
blocks = user.info.blocks || []
|
blocks = user.info.blocks || []
|
||||||
mutes = user.info.mutes || []
|
mutes = user.info.mutes || []
|
||||||
reblog_mutes = user.info.muted_reblogs || []
|
reblog_mutes = user.info.muted_reblogs || []
|
||||||
|
recipient_blocks = MapSet.new(blocks ++ mutes)
|
||||||
|
recipients = MapSet.new(item.recipients)
|
||||||
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
|
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
|
||||||
|
|
||||||
with parent when not is_nil(parent) <- Object.normalize(item),
|
with parent when not is_nil(parent) <- Object.normalize(item),
|
||||||
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
|
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
|
||||||
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
|
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
|
||||||
|
true <- MapSet.disjoint?(recipients, recipient_blocks),
|
||||||
%{host: item_host} <- URI.parse(item.actor),
|
%{host: item_host} <- URI.parse(item.actor),
|
||||||
%{host: parent_host} <- URI.parse(parent.data["actor"]),
|
%{host: parent_host} <- URI.parse(parent.data["actor"]),
|
||||||
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
|
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
|
||||||
|
@ -194,11 +201,8 @@ def push_to_socket(topics, topic, item) do
|
||||||
# Get the current user so we have up-to-date blocks etc.
|
# Get the current user so we have up-to-date blocks etc.
|
||||||
if socket_user do
|
if socket_user do
|
||||||
user = User.get_cached_by_ap_id(socket_user.ap_id)
|
user = User.get_cached_by_ap_id(socket_user.ap_id)
|
||||||
blocks = user.info.blocks || []
|
|
||||||
mutes = user.info.mutes || []
|
|
||||||
|
|
||||||
with true <- Enum.all?([blocks, mutes], &(item.actor not in &1)),
|
if should_send?(user, item) do
|
||||||
true <- thread_containment(item, user) do
|
|
||||||
send(transport_pid, {:text, StreamerView.render("update.json", item, user)})
|
send(transport_pid, {:text, StreamerView.render("update.json", item, user)})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -9,7 +9,14 @@ defmodule Pleroma.HealthcheckTest do
|
||||||
test "system_info/0" do
|
test "system_info/0" do
|
||||||
result = Healthcheck.system_info() |> Map.from_struct()
|
result = Healthcheck.system_info() |> Map.from_struct()
|
||||||
|
|
||||||
assert Map.keys(result) == [:active, :healthy, :idle, :memory_used, :pool_size]
|
assert Map.keys(result) == [
|
||||||
|
:active,
|
||||||
|
:healthy,
|
||||||
|
:idle,
|
||||||
|
:job_queue_stats,
|
||||||
|
:memory_used,
|
||||||
|
:pool_size
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "check_health/1" do
|
describe "check_health/1" do
|
||||||
|
|
70
test/job_queue_monitor_test.exs
Normal file
70
test/job_queue_monitor_test.exs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.JobQueueMonitorTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias Pleroma.JobQueueMonitor
|
||||||
|
|
||||||
|
@success {:process_event, :success, 1337,
|
||||||
|
%{
|
||||||
|
args: %{"op" => "refresh_subscriptions"},
|
||||||
|
attempt: 1,
|
||||||
|
id: 339,
|
||||||
|
max_attempts: 5,
|
||||||
|
queue: "federator_outgoing",
|
||||||
|
worker: "Pleroma.Workers.SubscriberWorker"
|
||||||
|
}}
|
||||||
|
|
||||||
|
@failure {:process_event, :failure, 22_521_134,
|
||||||
|
%{
|
||||||
|
args: %{"op" => "force_password_reset", "user_id" => "9nJG6n6Nbu7tj9GJX6"},
|
||||||
|
attempt: 1,
|
||||||
|
error: %RuntimeError{message: "oops"},
|
||||||
|
id: 345,
|
||||||
|
kind: :exception,
|
||||||
|
max_attempts: 1,
|
||||||
|
queue: "background",
|
||||||
|
stack: [
|
||||||
|
{Pleroma.Workers.BackgroundWorker, :perform, 2,
|
||||||
|
[file: 'lib/pleroma/workers/background_worker.ex', line: 31]},
|
||||||
|
{Oban.Queue.Executor, :safe_call, 1,
|
||||||
|
[file: 'lib/oban/queue/executor.ex', line: 42]},
|
||||||
|
{:timer, :tc, 3, [file: 'timer.erl', line: 197]},
|
||||||
|
{Oban.Queue.Executor, :call, 2, [file: 'lib/oban/queue/executor.ex', line: 23]},
|
||||||
|
{Task.Supervised, :invoke_mfa, 2, [file: 'lib/task/supervised.ex', line: 90]},
|
||||||
|
{:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
|
||||||
|
],
|
||||||
|
worker: "Pleroma.Workers.BackgroundWorker"
|
||||||
|
}}
|
||||||
|
|
||||||
|
test "stats/0" do
|
||||||
|
assert %{processed_jobs: _, queues: _, workers: _} = JobQueueMonitor.stats()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_cast/2" do
|
||||||
|
state = %{workers: %{}, queues: %{}, processed_jobs: 0}
|
||||||
|
|
||||||
|
assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state)
|
||||||
|
assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state)
|
||||||
|
assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state)
|
||||||
|
assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state)
|
||||||
|
|
||||||
|
assert state == %{
|
||||||
|
processed_jobs: 4,
|
||||||
|
queues: %{
|
||||||
|
"background" => %{failure: 2, processed_jobs: 2, success: 0},
|
||||||
|
"federator_outgoing" => %{failure: 0, processed_jobs: 2, success: 2}
|
||||||
|
},
|
||||||
|
workers: %{
|
||||||
|
"Pleroma.Workers.BackgroundWorker" => %{
|
||||||
|
"force_password_reset" => %{failure: 2, processed_jobs: 2, success: 0}
|
||||||
|
},
|
||||||
|
"Pleroma.Workers.SubscriberWorker" => %{
|
||||||
|
"refresh_subscriptions" => %{failure: 0, processed_jobs: 2, success: 2}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -1725,4 +1725,61 @@ test "update_info/2" do
|
||||||
assert %{info: %{hide_follows: true}} = Repo.get(User, user.id)
|
assert %{info: %{hide_follows: true}} = Repo.get(User, user.id)
|
||||||
assert {:ok, %{info: %{hide_follows: true}}} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
|
assert {:ok, %{info: %{hide_follows: true}}} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "get_cached_by_nickname_or_id" do
|
||||||
|
setup do
|
||||||
|
limit_to_local_content = Pleroma.Config.get([:instance, :limit_to_local_content])
|
||||||
|
local_user = insert(:user)
|
||||||
|
remote_user = insert(:user, nickname: "nickname@example.com", local: false)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local_content)
|
||||||
|
end)
|
||||||
|
|
||||||
|
[local_user: local_user, remote_user: remote_user]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
|
||||||
|
remote_user: remote_user
|
||||||
|
} do
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], false)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], true)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated",
|
||||||
|
%{remote_user: remote_user} do
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||||
|
assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated",
|
||||||
|
%{remote_user: remote_user, local_user: local_user} do
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "disallows getting remote users by nickname when :limit_to_local_content is set to true",
|
||||||
|
%{remote_user: remote_user} do
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], true)
|
||||||
|
assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows getting local users by nickname no matter what :limit_to_local_content is set to",
|
||||||
|
%{local_user: local_user} do
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], false)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], true)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||||
|
assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -225,69 +225,6 @@ test "cached purged after object deletion", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/object/:uuid/likes" do
|
|
||||||
setup do
|
|
||||||
like = insert(:like_activity)
|
|
||||||
like_object_ap_id = Object.normalize(like).data["id"]
|
|
||||||
|
|
||||||
uuid =
|
|
||||||
like_object_ap_id
|
|
||||||
|> String.split("/")
|
|
||||||
|> List.last()
|
|
||||||
|
|
||||||
[id: like.data["id"], uuid: uuid]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns the like activities in a collection", %{conn: conn, id: id, uuid: uuid} do
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|
||||||
|> get("/objects/#{uuid}/likes")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert List.first(result["first"]["orderedItems"])["id"] == id
|
|
||||||
assert result["type"] == "OrderedCollection"
|
|
||||||
assert result["totalItems"] == 1
|
|
||||||
refute result["first"]["next"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it does not crash when page number is exceeded total pages", %{conn: conn, uuid: uuid} do
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|
||||||
|> get("/objects/#{uuid}/likes?page=2")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert result["type"] == "OrderedCollectionPage"
|
|
||||||
assert result["totalItems"] == 1
|
|
||||||
refute result["next"]
|
|
||||||
assert Enum.empty?(result["orderedItems"])
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it contains the next key when likes count is more than 10", %{conn: conn} do
|
|
||||||
note = insert(:note_activity)
|
|
||||||
insert_list(11, :like_activity, note_activity: note)
|
|
||||||
|
|
||||||
uuid =
|
|
||||||
note
|
|
||||||
|> Object.normalize()
|
|
||||||
|> Map.get(:data)
|
|
||||||
|> Map.get("id")
|
|
||||||
|> String.split("/")
|
|
||||||
|> List.last()
|
|
||||||
|
|
||||||
result =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|
||||||
|> get("/objects/#{uuid}/likes?page=1")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert result["totalItems"] == 11
|
|
||||||
assert length(result["orderedItems"]) == 10
|
|
||||||
assert result["next"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "/activities/:uuid" do
|
describe "/activities/:uuid" do
|
||||||
test "it returns a json representation of the activity", %{conn: conn} do
|
test "it returns a json representation of the activity", %{conn: conn} do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
|
@ -811,10 +811,11 @@ test "unliking a previously liked object" do
|
||||||
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
||||||
assert object.data["like_count"] == 1
|
assert object.data["like_count"] == 1
|
||||||
|
|
||||||
{:ok, _, _, object} = ActivityPub.unlike(user, object)
|
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||||
assert object.data["like_count"] == 0
|
assert object.data["like_count"] == 0
|
||||||
|
|
||||||
assert Activity.get_by_id(like_activity.id) == nil
|
assert Activity.get_by_id(like_activity.id) == nil
|
||||||
|
assert note_activity.actor in unlike_activity.recipients
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -890,7 +891,7 @@ test "unannouncing a previously announced object" do
|
||||||
|
|
||||||
assert unannounce_activity.data["to"] == [
|
assert unannounce_activity.data["to"] == [
|
||||||
User.ap_followers(user),
|
User.ap_followers(user),
|
||||||
announce_activity.data["actor"]
|
object.data["actor"]
|
||||||
]
|
]
|
||||||
|
|
||||||
assert unannounce_activity.data["type"] == "Undo"
|
assert unannounce_activity.data["type"] == "Undo"
|
||||||
|
|
|
@ -546,6 +546,8 @@ test "it works for incoming update activities" do
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
||||||
|
|
||||||
|
assert data["id"] == update_data["id"]
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(data["actor"])
|
user = User.get_cached_by_ap_id(data["actor"])
|
||||||
assert user.name == "gargle"
|
assert user.name == "gargle"
|
||||||
|
|
||||||
|
|
|
@ -106,11 +106,13 @@ test "returns data for unlike activity" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
|
like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
|
||||||
|
|
||||||
|
object = Object.normalize(like_activity.data["object"])
|
||||||
|
|
||||||
assert Utils.make_unlike_data(user, like_activity, nil) == %{
|
assert Utils.make_unlike_data(user, like_activity, nil) == %{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"object" => like_activity.data,
|
"object" => like_activity.data,
|
||||||
"to" => [user.follower_address, like_activity.data["actor"]],
|
"to" => [user.follower_address, object.data["actor"]],
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => like_activity.data["context"]
|
"context" => like_activity.data["context"]
|
||||||
}
|
}
|
||||||
|
@ -119,7 +121,7 @@ test "returns data for unlike activity" do
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"object" => like_activity.data,
|
"object" => like_activity.data,
|
||||||
"to" => [user.follower_address, like_activity.data["actor"]],
|
"to" => [user.follower_address, object.data["actor"]],
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => like_activity.data["context"],
|
"context" => like_activity.data["context"],
|
||||||
"id" => "9mJEZK0tky1w2xD2vY"
|
"id" => "9mJEZK0tky1w2xD2vY"
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
clear_config([:instance, :safe_dm_mentions])
|
clear_config([:instance, :safe_dm_mentions])
|
||||||
clear_config([:instance, :limit])
|
clear_config([:instance, :limit])
|
||||||
clear_config([:instance, :max_pinned_statuses])
|
clear_config([:instance, :max_pinned_statuses])
|
||||||
|
@ -96,11 +98,13 @@ test "it adds emoji in the object" do
|
||||||
test "it adds emoji when updating profiles" do
|
test "it adds emoji when updating profiles" do
|
||||||
user = insert(:user, %{name: ":firefox:"})
|
user = insert(:user, %{name: ":firefox:"})
|
||||||
|
|
||||||
CommonAPI.update(user)
|
{:ok, activity} = CommonAPI.update(user)
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
user = User.get_cached_by_ap_id(user.ap_id)
|
||||||
[firefox] = user.info.source_data["tag"]
|
[firefox] = user.info.source_data["tag"]
|
||||||
|
|
||||||
assert firefox["name"] == ":firefox:"
|
assert firefox["name"] == ":firefox:"
|
||||||
|
|
||||||
|
assert Pleroma.Constants.as_public() in activity.recipients
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "posting" do
|
describe "posting" do
|
||||||
|
|
|
@ -100,5 +100,11 @@ test "Follow notification" do
|
||||||
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
||||||
|
|
||||||
assert [expected] == result
|
assert [expected] == result
|
||||||
|
|
||||||
|
User.perform(:delete, follower)
|
||||||
|
notification = Notification |> Repo.one() |> Repo.preload(:activity)
|
||||||
|
|
||||||
|
assert [] ==
|
||||||
|
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -233,30 +233,68 @@ test "it sends message if recipients invalid and thread containment is enabled b
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't send to blocked users" do
|
describe "blocks" do
|
||||||
user = insert(:user)
|
test "it doesn't send messages involving blocked users" do
|
||||||
blocked_user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.block(user, blocked_user)
|
blocked_user = insert(:user)
|
||||||
|
{:ok, user} = User.block(user, blocked_user)
|
||||||
|
|
||||||
task =
|
task =
|
||||||
Task.async(fn ->
|
Task.async(fn ->
|
||||||
refute_receive {:text, _}, 1_000
|
refute_receive {:text, _}, 1_000
|
||||||
end)
|
end)
|
||||||
|
|
||||||
fake_socket = %StreamerSocket{
|
fake_socket = %StreamerSocket{
|
||||||
transport_pid: task.pid,
|
transport_pid: task.pid,
|
||||||
user: user
|
user: user
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
|
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
|
||||||
|
|
||||||
topics = %{
|
topics = %{
|
||||||
"public" => [fake_socket]
|
"public" => [fake_socket]
|
||||||
}
|
}
|
||||||
|
|
||||||
Worker.push_to_socket(topics, "public", activity)
|
Worker.push_to_socket(topics, "public", activity)
|
||||||
|
|
||||||
Task.await(task)
|
Task.await(task)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't send messages transitively involving blocked users" do
|
||||||
|
blocker = insert(:user)
|
||||||
|
blockee = insert(:user)
|
||||||
|
friend = insert(:user)
|
||||||
|
|
||||||
|
task =
|
||||||
|
Task.async(fn ->
|
||||||
|
refute_receive {:text, _}, 1_000
|
||||||
|
end)
|
||||||
|
|
||||||
|
fake_socket = %StreamerSocket{
|
||||||
|
transport_pid: task.pid,
|
||||||
|
user: blocker
|
||||||
|
}
|
||||||
|
|
||||||
|
topics = %{
|
||||||
|
"public" => [fake_socket]
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, blocker} = User.block(blocker, blockee)
|
||||||
|
|
||||||
|
{:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
|
||||||
|
|
||||||
|
Worker.push_to_socket(topics, "public", activity_one)
|
||||||
|
|
||||||
|
{:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
|
||||||
|
|
||||||
|
Worker.push_to_socket(topics, "public", activity_two)
|
||||||
|
|
||||||
|
{:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
|
||||||
|
|
||||||
|
Worker.push_to_socket(topics, "public", activity_three)
|
||||||
|
|
||||||
|
Task.await(task)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't send unwanted DMs to list" do
|
test "it doesn't send unwanted DMs to list" do
|
||||||
|
|
Loading…
Reference in a new issue