Merge branch 'develop' into issue/1411
This commit is contained in:
commit
88f7cf51d4
77 changed files with 1233 additions and 389 deletions
|
@ -5,7 +5,6 @@ CC-BY-SA-4.0
|
|||
COPYING
|
||||
*file
|
||||
elixir_buildpack.config
|
||||
docs/
|
||||
test/
|
||||
|
||||
# Required to get version
|
||||
|
|
|
@ -31,6 +31,7 @@ build:
|
|||
|
||||
benchmark:
|
||||
stage: benchmark
|
||||
when: manual
|
||||
variables:
|
||||
MIX_ENV: benchmark
|
||||
services:
|
||||
|
@ -55,6 +56,19 @@ unit-testing:
|
|||
- mix ecto.migrate
|
||||
- mix coveralls --preload-modules
|
||||
|
||||
federated-testing:
|
||||
stage: test
|
||||
services:
|
||||
- name: minibikini/postgres-with-rum:12
|
||||
alias: postgres
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
script:
|
||||
- mix deps.get
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
- epmd -daemon
|
||||
- mix test --trace --only federated
|
||||
|
||||
unit-testing-rum:
|
||||
stage: test
|
||||
services:
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Admin API: Return `total` when querying for reports
|
||||
- Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
|
||||
- Admin API: Return link alongside with token on password reset
|
||||
- Admin API: Support authentication via `x-admin-token` HTTP header
|
||||
- Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
|
||||
- Mastodon API: `pleroma.thread_muted` to the Status entity
|
||||
- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
|
||||
|
@ -41,6 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Authentication: Added rate limit for password-authorized actions / login existence checks
|
||||
- Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.
|
||||
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
||||
- Mix task to list all users (`mix pleroma.user list`)
|
||||
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
|
||||
- MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
|
||||
<details>
|
||||
|
@ -68,6 +70,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
||||
- Configuration: `feed` option for user atom feed.
|
||||
- Pleroma API: Add Emoji reactions
|
||||
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
||||
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
|
||||
</details>
|
||||
|
||||
### Fixed
|
||||
|
@ -78,8 +82,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
- 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`
|
||||
- Admin API: Error when trying to update reports in the "old" format
|
||||
</details>
|
||||
|
||||
## [1.1.6] - 2019-11-19
|
||||
### Fixed
|
||||
- Not being able to log into to third party apps when the browser is logged into mastofe
|
||||
- Email confirmation not being required even when enabled
|
||||
- Mastodon API: conversations API crashing when one status is malformed
|
||||
|
||||
### Bundled Pleroma-FE Changes
|
||||
#### Added
|
||||
- About page
|
||||
- Meme arrows
|
||||
|
||||
#### Fixed
|
||||
- Image modal not closing unless clicked outside of image
|
||||
- Attachment upload spinner not being centered
|
||||
- Showing follow counters being 0 when they are actually hidden
|
||||
|
||||
## [1.1.5] - 2019-11-09
|
||||
### Fixed
|
||||
- Polls having different numbers in timelines/notifications/poll api endpoints due to cache desyncronization
|
||||
|
|
|
@ -95,7 +95,36 @@ defmodule Pleroma.LoadTesting.Fetcher do
|
|||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end
|
||||
end,
|
||||
"Rendering favorites timeline" => fn ->
|
||||
conn = Phoenix.ConnTest.build_conn(:get, "http://localhost:4001/api/v1/favourites", nil)
|
||||
Pleroma.Web.MastodonAPI.StatusController.favourites(
|
||||
%Plug.Conn{conn |
|
||||
assigns: %{user: user},
|
||||
query_params: %{"limit" => "0"},
|
||||
body_params: %{},
|
||||
cookies: %{},
|
||||
params: %{},
|
||||
path_params: %{},
|
||||
private: %{
|
||||
Pleroma.Web.Router => {[], %{}},
|
||||
phoenix_router: Pleroma.Web.Router,
|
||||
phoenix_action: :favourites,
|
||||
phoenix_controller: Pleroma.Web.MastodonAPI.StatusController,
|
||||
phoenix_endpoint: Pleroma.Web.Endpoint,
|
||||
phoenix_format: "json",
|
||||
phoenix_layout: {Pleroma.Web.LayoutView, "app.html"},
|
||||
phoenix_recycled: true,
|
||||
|
||||
phoenix_view: Pleroma.Web.MastodonAPI.StatusView,
|
||||
plug_session: %{"user_id" => user.id},
|
||||
plug_session_fetch: :done,
|
||||
plug_session_info: :write,
|
||||
plug_skip_csrf_protection: true
|
||||
}
|
||||
},
|
||||
%{})
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,24 @@ defmodule Pleroma.LoadTesting.Generator do
|
|||
use Pleroma.LoadTesting.Helper
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
def generate_like_activities(user, posts) do
|
||||
count_likes = Kernel.trunc(length(posts) / 4)
|
||||
IO.puts("Starting generating #{count_likes} like activities...")
|
||||
|
||||
{time, _} =
|
||||
:timer.tc(fn ->
|
||||
Task.async_stream(
|
||||
Enum.take_random(posts, count_likes),
|
||||
fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end,
|
||||
max_concurrency: 10,
|
||||
timeout: 30_000
|
||||
)
|
||||
|> Stream.run()
|
||||
end)
|
||||
|
||||
IO.puts("Inserting like activities take #{to_sec(time)} sec.\n")
|
||||
end
|
||||
|
||||
def generate_users(opts) do
|
||||
IO.puts("Starting generating #{opts[:users_max]} users...")
|
||||
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
||||
|
@ -31,7 +49,6 @@ defmodule Pleroma.LoadTesting.Generator do
|
|||
password_hash:
|
||||
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
||||
bio: "Tester Number #{i}",
|
||||
info: %{},
|
||||
local: remote
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,10 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do
|
|||
|
||||
generate_remote_activities(user, remote_users)
|
||||
|
||||
generate_like_activities(
|
||||
user, Pleroma.Repo.all(Pleroma.Activity.Queries.by_type("Create"))
|
||||
)
|
||||
|
||||
generate_dms(user, users, opts)
|
||||
|
||||
{:ok, activity} = generate_long_thread(user, users, opts)
|
||||
|
|
|
@ -180,7 +180,8 @@ config :pleroma, Pleroma.Web.Endpoint,
|
|||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, :console,
|
||||
format: "$time $metadata[$level] $message\n",
|
||||
level: :debug,
|
||||
format: "\n$time $metadata[$level] $message\n",
|
||||
metadata: [:request_id]
|
||||
|
||||
config :logger, :ex_syslogger,
|
||||
|
@ -208,6 +209,7 @@ config :tesla, adapter: Tesla.Adapter.Hackney
|
|||
config :pleroma, :http,
|
||||
proxy_url: nil,
|
||||
send_user_agent: true,
|
||||
user_agent: :default,
|
||||
adapter: [
|
||||
ssl_options: [
|
||||
# Workaround for remote server certificate chain issues
|
||||
|
|
|
@ -20,7 +20,8 @@ config :pleroma, Pleroma.Web.Endpoint,
|
|||
config :phoenix, serve_endpoints: true
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, level: :warn
|
||||
config :logger, :console, level: :warn
|
||||
config :logger, :ex_syslogger, level: :warn
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Config
|
||||
|
||||
config :pleroma, :instance, static: "/var/lib/pleroma/static"
|
||||
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||
|
||||
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
||||
|
|
|
@ -15,7 +15,9 @@ config :pleroma, Pleroma.Captcha,
|
|||
method: Pleroma.Captcha.Mock
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
config :logger, :console,
|
||||
level: :warn,
|
||||
format: "\n[$level] $message\n"
|
||||
|
||||
config :pleroma, :auth, oauth_consumer_strategies: []
|
||||
|
||||
|
|
|
@ -235,14 +235,6 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
}
|
||||
```
|
||||
|
||||
## DEPRECATED `PATCH /api/pleroma/admin/users/:nickname/activation_status`
|
||||
|
||||
### Active or deactivate a user
|
||||
|
||||
- Params:
|
||||
- `nickname`
|
||||
- `status` BOOLEAN field, false value means deactivation.
|
||||
|
||||
## `GET /api/pleroma/admin/users/:nickname_or_id`
|
||||
|
||||
### Retrive the details of a user
|
||||
|
@ -878,3 +870,19 @@ Compile time settings (need instance reboot):
|
|||
- Authentication: required
|
||||
- Params: None
|
||||
- Response: JSON, "ok" and 200 status
|
||||
|
||||
## `PATCH /api/pleroma/admin/users/confirm_email`
|
||||
|
||||
### Confirm users' emails
|
||||
|
||||
- Params:
|
||||
- `nicknames`
|
||||
- Response: Array of user nicknames
|
||||
|
||||
## `PATCH /api/pleroma/admin/users/resend_confirmation_email`
|
||||
|
||||
### Resend confirmation email
|
||||
|
||||
- Params:
|
||||
- `nicknames`
|
||||
- Response: Array of user nicknames
|
||||
|
|
|
@ -15,6 +15,11 @@ $PREFIX new <nickname> <email> [<options>]
|
|||
- `--admin`/`--no-admin` - whether the user should be an admin
|
||||
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
||||
|
||||
## List local users
|
||||
```sh
|
||||
$PREFIX list
|
||||
```
|
||||
|
||||
## Generate an invite link
|
||||
```sh
|
||||
$PREFIX invite [<options>]
|
||||
|
|
|
@ -348,7 +348,17 @@ Available caches:
|
|||
* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration).
|
||||
* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds).
|
||||
|
||||
## :hackney_pools
|
||||
## HTTP client
|
||||
|
||||
### :http
|
||||
|
||||
* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`)
|
||||
* `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`)
|
||||
* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
|
||||
* `adapter`: array of hackney options
|
||||
|
||||
|
||||
### :hackney_pools
|
||||
|
||||
Advanced. Tweaks Hackney (http client) connections pools.
|
||||
|
||||
|
@ -656,7 +666,7 @@ Feel free to adjust the priv_dir and port number. Then you will have to create t
|
|||
|
||||
### :admin_token
|
||||
|
||||
Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter. Example:
|
||||
Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter or `x-admin-token` HTTP header. Example:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :admin_token, "somerandomtoken"
|
||||
|
@ -664,8 +674,14 @@ config :pleroma, :admin_token, "somerandomtoken"
|
|||
|
||||
You can then do
|
||||
|
||||
```sh
|
||||
curl "http://localhost:4000/api/pleroma/admin/invite_token?admin_token=somerandomtoken"
|
||||
```shell
|
||||
curl "http://localhost:4000/api/pleroma/admin/users/invites?admin_token=somerandomtoken"
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites"
|
||||
```
|
||||
|
||||
### :auth
|
||||
|
|
|
@ -6,6 +6,11 @@ defmodule Mix.Pleroma do
|
|||
@doc "Common functions to be reused in mix tasks"
|
||||
def start_pleroma do
|
||||
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
||||
|
||||
if Pleroma.Config.get(:env) != :test do
|
||||
Application.put_env(:logger, :console, level: :debug)
|
||||
end
|
||||
|
||||
{:ok, _} = Application.ensure_all_started(:pleroma)
|
||||
end
|
||||
|
||||
|
|
|
@ -364,6 +364,24 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def run(["list"]) do
|
||||
start_pleroma()
|
||||
|
||||
Pleroma.User.Query.build(%{local: true})
|
||||
|> Pleroma.RepoStreamer.chunk_stream(500)
|
||||
|> Stream.each(fn users ->
|
||||
users
|
||||
|> Enum.each(fn user ->
|
||||
shell_info(
|
||||
"#{user.nickname} moderator: #{user.info.is_moderator}, admin: #{user.info.is_admin}, locked: #{
|
||||
user.info.locked
|
||||
}, deactivated: #{user.info.deactivated}"
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|> Stream.run()
|
||||
end
|
||||
|
||||
defp set_moderator(user, value) do
|
||||
{:ok, user} =
|
||||
user
|
||||
|
|
|
@ -303,4 +303,17 @@ defmodule Pleroma.Activity do
|
|||
end
|
||||
|
||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||
|
||||
def direct_conversation_id(activity, for_user) do
|
||||
alias Pleroma.Conversation.Participation
|
||||
|
||||
with %{data: %{"context" => context}} when is_binary(context) <- activity,
|
||||
%Pleroma.Conversation{} = conversation <- Pleroma.Conversation.get_for_ap_id(context),
|
||||
%Participation{id: participation_id} <-
|
||||
Participation.for_user_and_conversation(for_user, conversation) do
|
||||
participation_id
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,8 +17,14 @@ defmodule Pleroma.Application do
|
|||
def repository, do: @repository
|
||||
|
||||
def user_agent do
|
||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
case Pleroma.Config.get([:http, :user_agent], :default) do
|
||||
:default ->
|
||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
|
||||
custom ->
|
||||
custom
|
||||
end
|
||||
end
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||
|
|
|
@ -101,7 +101,7 @@ defmodule Pleroma.FollowingRelationship do
|
|||
|> select([r, u], u.follower_address)
|
||||
|> Repo.all()
|
||||
|
||||
if not user.local or user.nickname in [nil, "internal.fetch"] do
|
||||
if not user.local or user.invisible do
|
||||
following
|
||||
else
|
||||
[user.follower_address | following]
|
||||
|
|
|
@ -91,6 +91,7 @@ defmodule Pleroma.HTML do
|
|||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
result =
|
||||
content
|
||||
|> HtmlEntities.decode()
|
||||
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]")
|
||||
|> Floki.attribute("a", "href")
|
||||
|> Enum.at(0)
|
||||
|
|
|
@ -624,7 +624,31 @@ defmodule Pleroma.ModerationLog do
|
|||
"subject" => subjects
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} force password reset for users: #{users_to_nicknames_string(subjects)}"
|
||||
"@#{actor_nickname} forced password reset for users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "confirm_email",
|
||||
"subject" => subjects
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} confirmed email for users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "resend_confirmation_email",
|
||||
"subject" => subjects
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} re-sent confirmation email for users: #{
|
||||
users_to_nicknames_string(subjects)
|
||||
}"
|
||||
end
|
||||
|
||||
defp nicknames_to_string(nicknames) do
|
||||
|
|
|
@ -80,7 +80,7 @@ defmodule Pleroma.Object do
|
|||
end
|
||||
|
||||
defp warn_on_no_object_preloaded(ap_id) do
|
||||
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object"
|
||||
"Object.normalize() called without preloaded object (#{inspect(ap_id)}). Consider preloading the object"
|
||||
|> Logger.debug()
|
||||
|
||||
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||
|
@ -272,4 +272,8 @@ defmodule Pleroma.Object do
|
|||
|> Object.change(%{data: Map.merge(data || %{}, attrs)})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def local?(%Object{data: %{"id" => id}}) do
|
||||
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,7 +49,7 @@ defmodule Pleroma.Object.Fetcher do
|
|||
end
|
||||
|
||||
def refetch_object(%Object{data: %{"id" => id}} = object) do
|
||||
with {:local, false} <- {:local, String.starts_with?(id, Pleroma.Web.base_url() <> "/")},
|
||||
with {:local, false} <- {:local, Object.local?(object)},
|
||||
{:ok, data} <- fetch_and_contain_remote_object_from_id(id),
|
||||
{:ok, object} <- reinject_object(object, data) do
|
||||
{:ok, object}
|
||||
|
|
|
@ -16,14 +16,28 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
|
|||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(%{params: %{"admin_token" => admin_token}} = conn, _) do
|
||||
if secret_token() && admin_token == secret_token() do
|
||||
conn
|
||||
|> assign(:user, %User{is_admin: true})
|
||||
def call(conn, _) do
|
||||
if secret_token() do
|
||||
authenticate(conn)
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _), do: conn
|
||||
def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
|
||||
if admin_token == secret_token() do
|
||||
assign(conn, :user, %User{is_admin: true})
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate(conn) do
|
||||
token = secret_token()
|
||||
|
||||
case get_req_header(conn, "x-admin-token") do
|
||||
[^token] -> assign(conn, :user, %User{is_admin: true})
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -67,8 +67,7 @@ defmodule Pleroma.User do
|
|||
field(:source_data, :map, default: %{})
|
||||
field(:note_count, :integer, default: 0)
|
||||
field(:follower_count, :integer, default: 0)
|
||||
# Should be filled in only for remote users
|
||||
field(:following_count, :integer, default: nil)
|
||||
field(:following_count, :integer, default: 0)
|
||||
field(:locked, :boolean, default: false)
|
||||
field(:confirmation_pending, :boolean, default: false)
|
||||
field(:password_reset_pending, :boolean, default: false)
|
||||
|
@ -119,8 +118,6 @@ defmodule Pleroma.User do
|
|||
has_many(:registrations, Registration)
|
||||
has_many(:deliveries, Delivery)
|
||||
|
||||
field(:info, :map, default: %{})
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
@ -134,6 +131,8 @@ defmodule Pleroma.User do
|
|||
|
||||
def visible_for?(user, for_user \\ nil)
|
||||
|
||||
def visible_for?(%User{invisible: true}, _), do: false
|
||||
|
||||
def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true
|
||||
|
||||
def visible_for?(%User{} = user, for_user) do
|
||||
|
@ -176,22 +175,6 @@ defmodule Pleroma.User do
|
|||
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
|
||||
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|
||||
|
||||
def user_info(%User{} = user, args \\ %{}) do
|
||||
following_count =
|
||||
Map.get(args, :following_count, user.following_count || following_count(user))
|
||||
|
||||
follower_count = Map.get(args, :follower_count, user.follower_count)
|
||||
|
||||
%{
|
||||
note_count: user.note_count,
|
||||
locked: user.locked,
|
||||
confirmation_pending: user.confirmation_pending,
|
||||
default_scope: user.default_scope
|
||||
}
|
||||
|> Map.put(:following_count, following_count)
|
||||
|> Map.put(:follower_count, follower_count)
|
||||
end
|
||||
|
||||
def follow_state(%User{} = user, %User{} = target) do
|
||||
case Utils.fetch_latest_follow(user, target) do
|
||||
%{data: %{"state" => state}} -> state
|
||||
|
@ -210,10 +193,6 @@ defmodule Pleroma.User do
|
|||
Cachex.put(:user_cache, "follow_state:#{user_ap_id}|#{target_ap_id}", state)
|
||||
end
|
||||
|
||||
def set_info_cache(user, args) do
|
||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
|
||||
end
|
||||
|
||||
@spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
|
||||
def restrict_deactivated(query) do
|
||||
from(u in query, where: u.deactivated != ^true)
|
||||
|
@ -244,7 +223,6 @@ defmodule Pleroma.User do
|
|||
|
||||
params =
|
||||
params
|
||||
|> Map.put(:info, params[:info] || %{})
|
||||
|> truncate_if_exists(:name, name_limit)
|
||||
|> truncate_if_exists(:bio, bio_limit)
|
||||
|> truncate_fields_param()
|
||||
|
@ -492,6 +470,10 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def try_send_confirmation_email(users) do
|
||||
Enum.each(users, &try_send_confirmation_email/1)
|
||||
end
|
||||
|
||||
def needs_update?(%User{local: true}), do: false
|
||||
|
||||
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
|
||||
|
@ -522,14 +504,9 @@ defmodule Pleroma.User do
|
|||
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
||||
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
||||
def follow_all(follower, followeds) do
|
||||
followeds =
|
||||
Enum.reject(followeds, fn followed ->
|
||||
blocks?(follower, followed) || blocks?(followed, follower)
|
||||
end)
|
||||
|
||||
Enum.each(followeds, &follow(follower, &1, "accept"))
|
||||
|
||||
Enum.each(followeds, &update_follower_count/1)
|
||||
followeds
|
||||
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
|
||||
|> Enum.each(&follow(follower, &1, "accept"))
|
||||
|
||||
set_cache(follower)
|
||||
end
|
||||
|
@ -549,11 +526,11 @@ defmodule Pleroma.User do
|
|||
true ->
|
||||
FollowingRelationship.follow(follower, followed, state)
|
||||
|
||||
follower = maybe_update_following_count(follower)
|
||||
|
||||
{:ok, _} = update_follower_count(followed)
|
||||
|
||||
set_cache(follower)
|
||||
follower
|
||||
|> update_following_count()
|
||||
|> set_cache()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -561,11 +538,12 @@ defmodule Pleroma.User do
|
|||
if following?(follower, followed) and follower.ap_id != followed.ap_id do
|
||||
FollowingRelationship.unfollow(follower, followed)
|
||||
|
||||
follower = maybe_update_following_count(follower)
|
||||
|
||||
{:ok, followed} = update_follower_count(followed)
|
||||
|
||||
set_cache(follower)
|
||||
{:ok, follower} =
|
||||
follower
|
||||
|> update_following_count()
|
||||
|> set_cache()
|
||||
|
||||
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
||||
else
|
||||
|
@ -615,7 +593,6 @@ defmodule Pleroma.User do
|
|||
def set_cache(%User{} = user) do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
|
@ -634,7 +611,6 @@ defmodule Pleroma.User do
|
|||
def invalidate_cache(user) do
|
||||
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
||||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||
Cachex.del(:user_cache, "user_info:#{user.id}")
|
||||
end
|
||||
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
|
@ -702,11 +678,6 @@ defmodule Pleroma.User do
|
|||
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
|
||||
end
|
||||
|
||||
def get_cached_user_info(user) do
|
||||
key = "user_info:#{user.id}"
|
||||
Cachex.fetch!(:user_cache, key, fn -> user_info(user) end)
|
||||
end
|
||||
|
||||
def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
|
||||
|
||||
def get_or_fetch_by_nickname(nickname) do
|
||||
|
@ -895,8 +866,8 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
@spec maybe_update_following_count(User.t()) :: User.t()
|
||||
def maybe_update_following_count(%User{local: false} = user) do
|
||||
@spec update_following_count(User.t()) :: User.t()
|
||||
def update_following_count(%User{local: false} = user) do
|
||||
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||
maybe_fetch_follow_information(user)
|
||||
else
|
||||
|
@ -904,7 +875,13 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def maybe_update_following_count(user), do: user
|
||||
def update_following_count(%User{local: true} = user) do
|
||||
following_count = FollowingRelationship.following_count(user)
|
||||
|
||||
user
|
||||
|> follow_information_changeset(%{following_count: following_count})
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
def set_unread_conversation_count(%User{local: true} = user) do
|
||||
unread_query = Participation.unread_conversation_count_for_user(user)
|
||||
|
@ -1097,7 +1074,12 @@ defmodule Pleroma.User do
|
|||
|
||||
def deactivate(%User{} = user, status) do
|
||||
with {:ok, user} <- set_activation_status(user, status) do
|
||||
Enum.each(get_followers(user), &invalidate_cache/1)
|
||||
user
|
||||
|> get_followers()
|
||||
|> Enum.filter(& &1.local)
|
||||
|> Enum.each(fn follower ->
|
||||
follower |> update_following_count() |> set_cache()
|
||||
end)
|
||||
|
||||
# Only update local user counts, remote will be update during the next pull.
|
||||
user
|
||||
|
@ -1226,7 +1208,7 @@ defmodule Pleroma.User do
|
|||
def external_users(opts \\ []) do
|
||||
query =
|
||||
external_users_query()
|
||||
|> select([u], struct(u, [:id, :ap_id, :info]))
|
||||
|> select([u], struct(u, [:id, :ap_id]))
|
||||
|
||||
query =
|
||||
if opts[:max_id],
|
||||
|
@ -1317,22 +1299,23 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
@doc "Creates an internal service actor by URI if missing. Optionally takes nickname for addressing."
|
||||
@doc """
|
||||
Creates an internal service actor by URI if missing.
|
||||
Optionally takes nickname for addressing.
|
||||
"""
|
||||
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
|
||||
with %User{} = user <- get_cached_by_ap_id(uri) do
|
||||
user
|
||||
else
|
||||
_ ->
|
||||
{:ok, user} =
|
||||
%User{}
|
||||
|> cast(%{}, [:ap_id, :nickname, :local])
|
||||
|> put_change(:ap_id, uri)
|
||||
|> put_change(:nickname, nickname)
|
||||
|> put_change(:local, true)
|
||||
|> put_change(:follower_address, uri <> "/followers")
|
||||
|> Repo.insert()
|
||||
with user when is_nil(user) <- get_cached_by_ap_id(uri) do
|
||||
{:ok, user} =
|
||||
%User{
|
||||
invisible: true,
|
||||
local: true,
|
||||
ap_id: uri,
|
||||
nickname: nickname,
|
||||
follower_address: uri <> "/followers"
|
||||
}
|
||||
|> Repo.insert()
|
||||
|
||||
user
|
||||
user
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1575,6 +1558,11 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec toggle_confirmation([User.t()]) :: [{:ok, User.t()} | {:error, Changeset.t()}]
|
||||
def toggle_confirmation(users) do
|
||||
Enum.map(users, &toggle_confirmation/1)
|
||||
end
|
||||
|
||||
def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
|
||||
mascot
|
||||
end
|
||||
|
|
|
@ -45,6 +45,7 @@ defmodule Pleroma.User.Search do
|
|||
for_user
|
||||
|> base_query(following)
|
||||
|> filter_blocked_user(for_user)
|
||||
|> filter_invisible_users()
|
||||
|> filter_blocked_domains(for_user)
|
||||
|> fts_search(query_string)
|
||||
|> trigram_rank(query_string)
|
||||
|
@ -98,6 +99,10 @@ defmodule Pleroma.User.Search do
|
|||
defp base_query(_user, false), do: User
|
||||
defp base_query(user, true), do: User.get_followers_query(user)
|
||||
|
||||
defp filter_invisible_users(query) do
|
||||
from(q in query, where: q.invisible == false)
|
||||
end
|
||||
|
||||
defp filter_blocked_user(query, %User{blocks: blocks})
|
||||
when length(blocks) > 0 do
|
||||
from(q in query, where: not (q.ap_id in ^blocks))
|
||||
|
|
|
@ -734,6 +734,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
def fetch_instance_activities(params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("instance", params["instance"])
|
||||
|> Map.put("whole_db", true)
|
||||
|
||||
fetch_activities([Pleroma.Constants.as_public()], params, :offset)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
defp user_activities_recipients(%{"godmode" => true}) do
|
||||
[]
|
||||
end
|
||||
|
@ -961,6 +972,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_muted_reblogs(query, _), do: query
|
||||
|
||||
defp restrict_instance(query, %{"instance" => instance}) do
|
||||
users =
|
||||
from(
|
||||
u in User,
|
||||
select: u.ap_id,
|
||||
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
||||
)
|
||||
|> Repo.all()
|
||||
|
||||
from(activity in query, where: activity.actor in ^users)
|
||||
end
|
||||
|
||||
defp restrict_instance(query, _), do: query
|
||||
|
||||
defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
|
||||
|
||||
defp exclude_poll_votes(query, _) do
|
||||
|
@ -1041,6 +1066,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> restrict_reblogs(opts)
|
||||
|> restrict_pinned(opts)
|
||||
|> restrict_muted_reblogs(opts)
|
||||
|> restrict_instance(opts)
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> exclude_poll_votes(opts)
|
||||
|> exclude_visibility(opts)
|
||||
|
@ -1226,13 +1252,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
defp collection_private(data) do
|
||||
if is_map(data["first"]) and
|
||||
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||
defp collection_private(%{"first" => first}) do
|
||||
if is_map(first) and
|
||||
first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||
{:ok, false}
|
||||
else
|
||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||
{:ok, false}
|
||||
else
|
||||
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||
|
@ -1247,6 +1273,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
defp collection_private(_data), do: {:ok, true}
|
||||
|
||||
def user_data_from_user_object(data) do
|
||||
with {:ok, data} <- MRF.filter(data),
|
||||
{:ok, data} <- object_to_user_data(data) do
|
||||
|
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
def user(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|
@ -53,6 +53,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> render("user.json", %{user: user})
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
%{local: false} -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
relay_ap_id()
|
||||
|> User.get_or_create_service_actor_by_ap_id()
|
||||
|
||||
{:ok, actor} = User.set_invisible(actor, true)
|
||||
actor
|
||||
end
|
||||
|
||||
|
|
|
@ -903,7 +903,13 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
def strip_report_status_data(activity) do
|
||||
[actor | reported_activities] = activity.data["object"]
|
||||
stripped_activities = Enum.map(reported_activities, & &1["id"])
|
||||
|
||||
stripped_activities =
|
||||
Enum.map(reported_activities, fn
|
||||
act when is_map(act) -> act["id"]
|
||||
act when is_binary(act) -> act
|
||||
end)
|
||||
|
||||
new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
|
||||
|
||||
{:ok, %{activity | data: new_data}}
|
||||
|
|
|
@ -227,6 +227,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_instance_activities(%{
|
||||
"instance" => instance,
|
||||
"limit" => page_size,
|
||||
"offset" => (page - 1) * page_size
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, as: :activity})
|
||||
end
|
||||
|
||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||
|
||||
|
@ -335,7 +350,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
}
|
||||
|
||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
||||
{:ok, users, count} <- filter_relay_user(users, count),
|
||||
{:ok, users, count} <- filter_service_users(users, count),
|
||||
do:
|
||||
conn
|
||||
|> json(
|
||||
|
@ -347,15 +362,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
)
|
||||
end
|
||||
|
||||
defp filter_relay_user(users, count) do
|
||||
filtered_users = Enum.reject(users, &relay_user?/1)
|
||||
count = if Enum.any?(users, &relay_user?/1), do: length(filtered_users), else: count
|
||||
defp filter_service_users(users, count) do
|
||||
filtered_users = Enum.reject(users, &service_user?/1)
|
||||
count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
|
||||
|
||||
{:ok, filtered_users, count}
|
||||
end
|
||||
|
||||
defp relay_user?(user) do
|
||||
user.ap_id == Relay.relay_ap_id()
|
||||
defp service_user?(user) do
|
||||
String.match?(user.ap_id, ~r/.*\/relay$/) or
|
||||
String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
|
||||
end
|
||||
|
||||
@filters ~w(local external active deactivated is_admin is_moderator)
|
||||
|
@ -799,6 +815,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
conn |> json("ok")
|
||||
end
|
||||
|
||||
def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||
|
||||
User.toggle_confirmation(users)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "confirm_email"
|
||||
})
|
||||
|
||||
conn |> json("")
|
||||
end
|
||||
|
||||
def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||
|
||||
User.try_send_confirmation_email(users)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "resend_confirmation_email"
|
||||
})
|
||||
|
||||
conn |> json("")
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|
|
|
@ -36,7 +36,8 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
"deactivated" => user.deactivated,
|
||||
"local" => user.local,
|
||||
"roles" => User.roles(user),
|
||||
"tags" => user.tags || []
|
||||
"tags" => user.tags || [],
|
||||
"confirmation_pending" => user.confirmation_pending
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
@doc "GET /api/v1/accounts/:id"
|
||||
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
||||
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do
|
||||
true <- User.visible_for?(user, for_user) do
|
||||
render(conn, "show.json", user: user, for: for_user)
|
||||
else
|
||||
_e -> render_error(conn, :not_found, "Can't find user")
|
||||
|
|
|
@ -71,18 +71,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|
||||
image = User.avatar_url(user) |> MediaProxy.url()
|
||||
header = User.banner_url(user) |> MediaProxy.url()
|
||||
user_info = User.get_cached_user_info(user)
|
||||
|
||||
following_count =
|
||||
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
|
||||
user_info.following_count
|
||||
user.following_count || 0
|
||||
else
|
||||
0
|
||||
end
|
||||
|
||||
followers_count =
|
||||
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
|
||||
user_info.follower_count
|
||||
user.follower_count || 0
|
||||
else
|
||||
0
|
||||
end
|
||||
|
@ -144,7 +143,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|
||||
# Pleroma extension
|
||||
pleroma: %{
|
||||
confirmation_pending: user_info.confirmation_pending,
|
||||
confirmation_pending: user.confirmation_pending,
|
||||
tags: user.tags,
|
||||
hide_followers_count: user.hide_followers_count,
|
||||
hide_follows_count: user.hide_follows_count,
|
||||
|
@ -157,7 +156,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
}
|
||||
}
|
||||
|> maybe_put_role(user, opts[:for])
|
||||
|> maybe_put_settings(user, opts[:for], user_info)
|
||||
|> maybe_put_settings(user, opts[:for], opts)
|
||||
|> maybe_put_notification_settings(user, opts[:for])
|
||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||
|
@ -191,7 +190,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
data,
|
||||
%User{id: user_id} = user,
|
||||
%User{id: user_id},
|
||||
_user_info
|
||||
_opts
|
||||
) do
|
||||
data
|
||||
|> Kernel.put_in([:source, :privacy], user.default_scope)
|
||||
|
|
|
@ -9,8 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
|
@ -245,12 +243,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
direct_conversation_id =
|
||||
with {_, nil} <- {:direct_conversation_id, opts[:direct_conversation_id]},
|
||||
{_, true} <- {:include_id, opts[:with_direct_conversation_id]},
|
||||
{_, %User{} = for_user} <- {:for_user, opts[:for]},
|
||||
%{data: %{"context" => context}} when is_binary(context) <- activity,
|
||||
%Conversation{} = conversation <- Conversation.get_for_ap_id(context),
|
||||
%Participation{id: participation_id} <-
|
||||
Participation.for_user_and_conversation(for_user, conversation) do
|
||||
participation_id
|
||||
{_, %User{} = for_user} <- {:for_user, opts[:for]} do
|
||||
Activity.direct_conversation_id(activity, for_user)
|
||||
else
|
||||
{:direct_conversation_id, participation_id} when is_integer(participation_id) ->
|
||||
participation_id
|
||||
|
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Metadata.PlayerView
|
||||
|
@ -38,11 +37,9 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
with id <- o_status_url(conn, :object, uuid),
|
||||
{_, %Activity{} = activity} <-
|
||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
"html" -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
_ -> represent_activity(conn, nil, activity, user)
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
|
@ -61,11 +58,9 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
||||
with id <- o_status_url(conn, :activity, uuid),
|
||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
"html" -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
_ -> represent_activity(conn, format, activity, user)
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
|
@ -81,7 +76,15 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
cond do
|
||||
format == "html" && activity.data["type"] == "Create" ->
|
||||
format in ["json", "activity+json"] ->
|
||||
if activity.local do
|
||||
%{data: %{"id" => redirect_url}} = Object.normalize(activity)
|
||||
redirect(conn, external: redirect_url)
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
|
||||
activity.data["type"] == "Create" ->
|
||||
%Object{} = object = Object.normalize(activity)
|
||||
|
||||
RedirectController.redirector_with_meta(
|
||||
|
@ -94,11 +97,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
}
|
||||
)
|
||||
|
||||
format == "html" ->
|
||||
RedirectController.redirector(conn, nil)
|
||||
|
||||
true ->
|
||||
represent_activity(conn, format, activity, user)
|
||||
RedirectController.redirector(conn, nil)
|
||||
end
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
|
@ -135,24 +135,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
end
|
||||
end
|
||||
|
||||
defp represent_activity(
|
||||
conn,
|
||||
"activity+json",
|
||||
%Activity{data: %{"type" => "Create"}} = activity,
|
||||
_user
|
||||
) do
|
||||
object = Object.normalize(activity)
|
||||
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_view(ObjectView)
|
||||
|> render("object.json", %{object: object})
|
||||
end
|
||||
|
||||
defp represent_activity(_conn, _, _, _) do
|
||||
{:error, :not_found}
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
render_error(conn, :not_found, "Not found")
|
||||
end
|
||||
|
|
|
@ -33,6 +33,8 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||
avatar_url = User.avatar_url(actor)
|
||||
object = Object.normalize(activity)
|
||||
user = User.get_cached_by_id(user_id)
|
||||
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
||||
|
||||
for subscription <- fetch_subsriptions(user_id),
|
||||
get_in(subscription.data, ["alerts", type]) do
|
||||
|
@ -45,7 +47,8 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
icon: avatar_url,
|
||||
preferred_locale: "en",
|
||||
pleroma: %{
|
||||
activity_id: activity_id
|
||||
activity_id: activity_id,
|
||||
direct_conversation_id: direct_conversation_id
|
||||
}
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
|
|
@ -178,6 +178,11 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||
|
||||
get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
|
||||
|
||||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||
|
||||
get("/reports", AdminAPIController, :list_reports)
|
||||
get("/grouped_reports", AdminAPIController, :list_grouped_reports)
|
||||
get("/reports/:id", AdminAPIController, :report_show)
|
||||
|
|
24
mix.exs
24
mix.exs
|
@ -102,7 +102,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:ecto_sql, "~> 3.2"},
|
||||
{:postgrex, ">= 0.13.5"},
|
||||
{:oban, "~> 0.8.1"},
|
||||
{:oban, "~> 0.12.0"},
|
||||
{:quantum, "~> 2.3"},
|
||||
{:gettext, "~> 0.15"},
|
||||
{:comeonin, "~> 4.1.1"},
|
||||
|
@ -194,27 +194,21 @@ defmodule Pleroma.Mixfile do
|
|||
identifier_filter = ~r/[^0-9a-z\-]+/i
|
||||
|
||||
# Pre-release version, denoted from patch version with a hyphen
|
||||
{git_tag, git_pre_release} =
|
||||
git_pre_release =
|
||||
with {tag, 0} <-
|
||||
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
|
||||
tag = String.trim(tag),
|
||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
|
||||
describe = String.trim(describe),
|
||||
ahead <- String.replace(describe, tag, ""),
|
||||
ahead <- String.trim_leading(ahead, "-") do
|
||||
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]) do
|
||||
describe
|
||||
|> String.trim()
|
||||
|> String.replace(String.trim(tag), "")
|
||||
|> String.trim_leading("-")
|
||||
|> String.trim()
|
||||
else
|
||||
_ ->
|
||||
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||
{nil, "0-g" <> String.trim(commit_hash)}
|
||||
"0-g" <> String.trim(commit_hash)
|
||||
end
|
||||
|
||||
if git_tag && version != git_tag do
|
||||
Mix.shell().error(
|
||||
"Application version #{inspect(version)} does not match git tag #{inspect(git_tag)}"
|
||||
)
|
||||
end
|
||||
|
||||
# Branch name as pre-release version component, denoted with a dot
|
||||
branch_name =
|
||||
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||
|
|
8
mix.lock
8
mix.lock
|
@ -23,8 +23,8 @@
|
|||
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
|
||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.2.3", "51274df79862845b388733fddcf6f107d0c8c86e27abe7131fa98f8d30761bda", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.0", "751cea597e8deb616084894dd75cbabfdbe7255ff01e8c058ca13f0353a3921b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.2.5", "76c864b77948a479e18e69cc1d0f0f4ee7cced1148ffe6a093ff91eba644f0b5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.2", "d10845bc147b9f61ef485cbf0973c0a337237199bd9bd30dd9542db00aadc26b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0 or ~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
|
||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
|
||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
||||
|
@ -67,7 +67,7 @@
|
|||
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
|
||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oban": {:hex, :oban, "0.8.1", "4bbf62eb1829f856d69aeb5069ac7036afe07db8221a17de2a9169cc7a58a318", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"oban": {:hex, :oban, "0.12.0", "5477d5ab4a5a201c0b6c89764040ebfc5d2c71c488a36f378016ce5990838f0f", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.10", "619e4a545505f562cd294df52294372d012823f4fd9d34a6657a8b242898c255", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -97,7 +97,7 @@
|
|||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"},
|
||||
"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"},
|
||||
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
|
||||
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"},
|
||||
"tesla": {:hex, :tesla, "1.3.0", "f35d72f029e608f9cdc6f6d6fcc7c66cf6d6512a70cfef9206b21b8bd0203a30", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 0.4", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
defmodule Pleroma.Repo.Migrations.SetVisibleServiceActors do
|
||||
use Ecto.Migration
|
||||
import Ecto.Query
|
||||
alias Pleroma.Repo
|
||||
|
||||
def up do
|
||||
user_nicknames = ["relay", "internal.fetch"]
|
||||
|
||||
from(
|
||||
u in "users",
|
||||
where: u.nickname in ^user_nicknames,
|
||||
update: [
|
||||
set: [invisible: true]
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([])
|
||||
end
|
||||
|
||||
def down do
|
||||
:ok
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Pleroma.Repo.Migrations.RemoveInfoFromUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
remove(:info, :map, default: %{})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
defmodule Pleroma.Repo.Migrations.FixMissingFollowingCount do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
"""
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
following_count = sub.count
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
users.id AS sub_id
|
||||
,COUNT (following_relationships.id)
|
||||
FROM
|
||||
following_relationships
|
||||
,users
|
||||
WHERE
|
||||
users.id = following_relationships.follower_id
|
||||
AND following_relationships.state = 'accept'
|
||||
GROUP BY
|
||||
users.id
|
||||
) AS sub
|
||||
WHERE
|
||||
users.id = sub.sub_id
|
||||
AND users.local = TRUE
|
||||
;
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
following_count = 0
|
||||
WHERE
|
||||
following_count IS NULL
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
execute("ALTER TABLE users
|
||||
ALTER COLUMN following_count SET DEFAULT 0,
|
||||
ALTER COLUMN following_count SET NOT NULL
|
||||
")
|
||||
end
|
||||
|
||||
def down do
|
||||
execute("ALTER TABLE users
|
||||
ALTER COLUMN following_count DROP DEFAULT,
|
||||
ALTER COLUMN following_count DROP NOT NULL
|
||||
")
|
||||
end
|
||||
end
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.fd71461124f3eb029b1b.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.5c3fab032deb5f2793cb.js></script><script type=text/javascript src=/static/js/app.105d64a8fcdd6724ccde.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.fd71461124f3eb029b1b.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.76db8e4cdf29decd5cab.js></script><script type=text/javascript src=/static/js/app.d20ca27d22d74eb7bce0.js></script></body></html>
|
|
@ -1,2 +1,2 @@
|
|||
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{1012:function(t,e,i){"use strict";i.r(e);var n=i(1013),c=i.n(n);for(var r in n)"default"!==r&&function(t){i.d(e,t,function(){return n[t]})}(r);var a=i(1016),s=i(0);var o=function(t){i(1014)},u=Object(s.a)(c.a,a.a,a.b,!1,o,null,null);e.default=u.exports},1013:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=c(i(348));function c(t){return t&&t.__esModule?t:{default:t}}var r={components:{TabSwitcher:c(i(205)).default},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,c=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var r=new File([t],e,{mimetype:"image/png"}),a=new FormData;a.append("file",r),n.default.uploadMedia({store:c,formData:a}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}};e.default=r},1014:function(t,e,i){var n=i(1015);"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals);(0,i(2).default)("cc6cdea4",n,!0,{})},1015:function(t,e,i){(t.exports=i(1)(!1)).push([t.i,".sticker-picker{width:100%;position:relative}.sticker-picker .tab-switcher{position:absolute;top:0;bottom:0;left:0;right:0}.sticker-picker .sticker-picker-content .sticker{display:inline-block;width:20%;height:20%}.sticker-picker .sticker-picker-content .sticker img{width:100%}.sticker-picker .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--link,#d8a070))}",""])},1016:function(t,e,i){"use strict";i.d(e,"a",function(){return n}),i.d(e,"b",function(){return c});var n=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(n){return i("div",{key:n,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+n,e.meta.title)}}},[i("img",{attrs:{src:e.path+n}})])}),0)}),0)],1)},c=[]}}]);
|
||||
//# sourceMappingURL=2.73375b727cef616c59b4.js.map
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{1023:function(t,e,i){"use strict";i.r(e);var n=i(1024),c=i.n(n);for(var r in n)"default"!==r&&function(t){i.d(e,t,function(){return n[t]})}(r);var a=i(1027),s=i(0);var o=function(t){i(1025)},u=Object(s.a)(c.a,a.a,a.b,!1,o,null,null);e.default=u.exports},1024:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=c(i(352));function c(t){return t&&t.__esModule?t:{default:t}}var r={components:{TabSwitcher:c(i(207)).default},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,c=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var r=new File([t],e,{mimetype:"image/png"}),a=new FormData;a.append("file",r),n.default.uploadMedia({store:c,formData:a}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}};e.default=r},1025:function(t,e,i){var n=i(1026);"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals);(0,i(2).default)("cc6cdea4",n,!0,{})},1026:function(t,e,i){(t.exports=i(1)(!1)).push([t.i,".sticker-picker{width:100%;position:relative}.sticker-picker .tab-switcher{position:absolute;top:0;bottom:0;left:0;right:0}.sticker-picker .sticker-picker-content .sticker{display:inline-block;width:20%;height:20%}.sticker-picker .sticker-picker-content .sticker img{width:100%}.sticker-picker .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--link,#d8a070))}",""])},1027:function(t,e,i){"use strict";i.d(e,"a",function(){return n}),i.d(e,"b",function(){return c});var n=function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(n){return i("div",{key:n,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+n,e.meta.title)}}},[i("img",{attrs:{src:e.path+n}})])}),0)}),0)],1)},c=[]}}]);
|
||||
//# sourceMappingURL=2.c96b30ae9f2d3f46f0ad.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
priv/static/static/js/app.d20ca27d22d74eb7bce0.js
Normal file
2
priv/static/static/js/app.d20ca27d22d74eb7bce0.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.d20ca27d22d74eb7bce0.js.map
Normal file
1
priv/static/static/js/app.d20ca27d22d74eb7bce0.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,4 @@
|
|||
var serviceWorkerOption = {"assets":["/static/img/nsfw.74818f9.png","/static/css/app.fd71461124f3eb029b1b.css","/static/js/app.105d64a8fcdd6724ccde.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.5c3fab032deb5f2793cb.js","/static/js/2.73375b727cef616c59b4.js"]};
|
||||
var serviceWorkerOption = {"assets":["/static/img/nsfw.74818f9.png","/static/css/app.fd71461124f3eb029b1b.css","/static/js/app.d20ca27d22d74eb7bce0.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.76db8e4cdf29decd5cab.js","/static/js/2.c96b30ae9f2d3f46f0ad.js"]};
|
||||
|
||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=0)}([function(e,n,t){"use strict";var r,o=t(1),i=(r=o)&&r.__esModule?r:{default:r};function a(){return clients.matchAll({includeUncontrolled:!0}).then(function(e){return e.filter(function(e){return"window"===e.type})})}self.addEventListener("push",function(e){e.data&&e.waitUntil(i.default.getItem("vuex-lz").then(function(e){return e.config.webPushNotifications}).then(function(n){return n&&a().then(function(n){var t=e.data.json();if(0===n.length)return self.registration.showNotification(t.title,t)})}))}),self.addEventListener("notificationclick",function(e){e.notification.close(),e.waitUntil(a().then(function(e){for(var n=0;n<e.length;n++){var t=e[n];if("/"===t.url&&"focus"in t)return t.focus()}if(clients.openWindow)return clients.openWindow("/")}))})},function(e,n){
|
||||
/*!
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
# fi
|
||||
|
||||
# Set the release to work across nodes
|
||||
export RELEASE_DISTRIBUTION=name
|
||||
export RELEASE_NODE=<%= @release.name %>@127.0.0.1
|
||||
export RELEASE_DISTRIBUTION="${RELEASE_DISTRIBUTION:-name}"
|
||||
export RELEASE_NODE="${RELEASE_NODE:-<%= @release.name %>@127.0.0.1}"
|
||||
|
|
47
test/federation/federation_test.exs
Normal file
47
test/federation/federation_test.exs
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Integration.FederationTest do
|
||||
use Pleroma.DataCase
|
||||
@moduletag :federated
|
||||
import Pleroma.Cluster
|
||||
|
||||
setup_all do
|
||||
Pleroma.Cluster.spawn_default_cluster()
|
||||
:ok
|
||||
end
|
||||
|
||||
@federated1 :"federated1@127.0.0.1"
|
||||
describe "federated cluster primitives" do
|
||||
test "within/2 captures local bindings and executes block on remote node" do
|
||||
captured_binding = :captured
|
||||
|
||||
result =
|
||||
within @federated1 do
|
||||
user = Pleroma.Factory.insert(:user)
|
||||
{captured_binding, node(), user}
|
||||
end
|
||||
|
||||
assert {:captured, @federated1, user} = result
|
||||
refute Pleroma.User.get_by_id(user.id)
|
||||
assert user.id == within(@federated1, do: Pleroma.User.get_by_id(user.id)).id
|
||||
end
|
||||
|
||||
test "runs webserver on customized port" do
|
||||
{nickname, url, url_404} =
|
||||
within @federated1 do
|
||||
import Pleroma.Web.Router.Helpers
|
||||
user = Pleroma.Factory.insert(:user)
|
||||
user_url = account_url(Pleroma.Web.Endpoint, :show, user)
|
||||
url_404 = account_url(Pleroma.Web.Endpoint, :show, "not-exists")
|
||||
|
||||
{user.nickname, user_url, url_404}
|
||||
end
|
||||
|
||||
assert {:ok, {{_, 200, _}, _headers, body}} = :httpc.request(~c"#{url}")
|
||||
assert %{"acct" => ^nickname} = Jason.decode!(body)
|
||||
assert {:ok, {{_, 404, _}, _headers, _body}} = :httpc.request(~c"#{url_404}")
|
||||
end
|
||||
end
|
||||
end
|
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||
"diaspora": "https://diasporafoundation.org/ns/",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"directMessage": "litepub:directMessage"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:8080/followers/fuser3",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 296
|
||||
}
|
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||
"diaspora": "https://diasporafoundation.org/ns/",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"directMessage": "litepub:directMessage"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:8080/following/fuser3",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 32
|
||||
}
|
47
test/following_relationship_test.exs
Normal file
47
test/following_relationship_test.exs
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.FollowingRelationshipTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "following/1" do
|
||||
test "returns following addresses without internal.fetch" do
|
||||
user = insert(:user)
|
||||
fetch_actor = InternalFetchActor.get_actor()
|
||||
FollowingRelationship.follow(fetch_actor, user, "accept")
|
||||
assert FollowingRelationship.following(fetch_actor) == [user.follower_address]
|
||||
end
|
||||
|
||||
test "returns following addresses without relay" do
|
||||
user = insert(:user)
|
||||
relay_actor = Relay.get_actor()
|
||||
FollowingRelationship.follow(relay_actor, user, "accept")
|
||||
assert FollowingRelationship.following(relay_actor) == [user.follower_address]
|
||||
end
|
||||
|
||||
test "returns following addresses without remote user" do
|
||||
user = insert(:user)
|
||||
actor = insert(:user, local: false)
|
||||
FollowingRelationship.follow(actor, user, "accept")
|
||||
assert FollowingRelationship.following(actor) == [user.follower_address]
|
||||
end
|
||||
|
||||
test "returns following addresses with local user" do
|
||||
user = insert(:user)
|
||||
actor = insert(:user, local: true)
|
||||
FollowingRelationship.follow(actor, user, "accept")
|
||||
|
||||
assert FollowingRelationship.following(actor) == [
|
||||
actor.follower_address,
|
||||
user.follower_address
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -228,5 +228,16 @@ defmodule Pleroma.HTMLTest do
|
|||
|
||||
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||
end
|
||||
|
||||
test "does not crash when there is an HTML entity in a link" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{"status" => "\"http://cofe.com/?boomer=ok&foo=bar\""})
|
||||
|
||||
object = Object.normalize(activity)
|
||||
|
||||
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,11 +16,21 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
|
|||
|
||||
test "send pleroma user agent" do
|
||||
Pleroma.Config.put([:http, :send_user_agent], true)
|
||||
Pleroma.Config.put([:http, :user_agent], :default)
|
||||
|
||||
assert RequestBuilder.headers(%{}, []) == %{
|
||||
headers: [{"User-Agent", Pleroma.Application.user_agent()}]
|
||||
}
|
||||
end
|
||||
|
||||
test "send custom user agent" do
|
||||
Pleroma.Config.put([:http, :send_user_agent], true)
|
||||
Pleroma.Config.put([:http, :user_agent], "totally-not-pleroma")
|
||||
|
||||
assert RequestBuilder.headers(%{}, []) == %{
|
||||
headers: [{"User-Agent", "totally-not-pleroma"}]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "add_optional_params/3" do
|
||||
|
|
|
@ -22,21 +22,39 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
|
|||
assert conn == ret_conn
|
||||
end
|
||||
|
||||
test "with secret set and given in the 'admin_token' parameter, it assigns an admin user", %{
|
||||
conn: conn
|
||||
} do
|
||||
Pleroma.Config.put(:admin_token, "password123")
|
||||
describe "when secret set it assigns an admin user" do
|
||||
test "with `admin_token` query parameter", %{conn: conn} do
|
||||
Pleroma.Config.put(:admin_token, "password123")
|
||||
|
||||
conn =
|
||||
%{conn | params: %{"admin_token" => "wrong_password"}}
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
conn =
|
||||
%{conn | params: %{"admin_token" => "wrong_password"}}
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
|
||||
refute conn.assigns[:user]
|
||||
refute conn.assigns[:user]
|
||||
|
||||
conn =
|
||||
%{conn | params: %{"admin_token" => "password123"}}
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
conn =
|
||||
%{conn | params: %{"admin_token" => "password123"}}
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user].is_admin
|
||||
assert conn.assigns[:user].is_admin
|
||||
end
|
||||
|
||||
test "with `x-admin-token` HTTP header", %{conn: conn} do
|
||||
Pleroma.Config.put(:admin_token, "☕️")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("x-admin-token", "🥛")
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
|
||||
refute conn.assigns[:user]
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("x-admin-token", "☕️")
|
||||
|> AdminSecretAuthenticationPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user].is_admin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
218
test/support/cluster.ex
Normal file
218
test/support/cluster.ex
Normal file
|
@ -0,0 +1,218 @@
|
|||
defmodule Pleroma.Cluster do
|
||||
@moduledoc """
|
||||
Facilities for managing a cluster of slave VM's for federated testing.
|
||||
|
||||
## Spawning the federated cluster
|
||||
|
||||
`spawn_cluster/1` spawns a map of slave nodes that are started
|
||||
within the running VM. During startup, the slave node is sent all configuration
|
||||
from the parent node, as well as all code. After receiving configuration and
|
||||
code, the slave then starts all applications currently running on the parent.
|
||||
The configuration passed to `spawn_cluster/1` overrides any parent application
|
||||
configuration for the provided OTP app and key. This is useful for customizing
|
||||
the Ecto database, Phoenix webserver ports, etc.
|
||||
|
||||
For example, to start a single federated VM named ":federated1", with the
|
||||
Pleroma Endpoint running on port 4123, and with a database named
|
||||
"pleroma_test1", you would run:
|
||||
|
||||
endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
|
||||
repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
|
||||
|
||||
Pleroma.Cluster.spawn_cluster(%{
|
||||
:"federated1@127.0.0.1" => [
|
||||
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test1")},
|
||||
{:pleroma, Pleroma.Web.Endpoint,
|
||||
Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
|
||||
]
|
||||
})
|
||||
|
||||
*Note*: application configuration for a given key is not merged,
|
||||
so any customization requires first fetching the existing values
|
||||
and merging yourself by providing the merged configuration,
|
||||
such as above with the endpoint config and repo config.
|
||||
|
||||
## Executing code within a remote node
|
||||
|
||||
Use the `within/2` macro to execute code within the context of a remote
|
||||
federated node. The code block captures all local variable bindings from
|
||||
the parent's context and returns the result of the expression after executing
|
||||
it on the remote node. For example:
|
||||
|
||||
import Pleroma.Cluster
|
||||
|
||||
parent_value = 123
|
||||
|
||||
result =
|
||||
within :"federated1@127.0.0.1" do
|
||||
{node(), parent_value}
|
||||
end
|
||||
|
||||
assert result == {:"federated1@127.0.0.1, 123}
|
||||
|
||||
*Note*: while local bindings are captured and available within the block,
|
||||
other parent contexts like required, aliased, or imported modules are not
|
||||
in scope. Those will need to be reimported/aliases/required within the block
|
||||
as `within/2` is a remote procedure call.
|
||||
"""
|
||||
|
||||
@extra_apps Pleroma.Mixfile.application()[:extra_applications]
|
||||
|
||||
@doc """
|
||||
Spawns the default Pleroma federated cluster.
|
||||
|
||||
Values before may be customized as needed for the test suite.
|
||||
"""
|
||||
def spawn_default_cluster do
|
||||
endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
|
||||
repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
|
||||
|
||||
spawn_cluster(%{
|
||||
:"federated1@127.0.0.1" => [
|
||||
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated1")},
|
||||
{:pleroma, Pleroma.Web.Endpoint,
|
||||
Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
|
||||
],
|
||||
:"federated2@127.0.0.1" => [
|
||||
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated2")},
|
||||
{:pleroma, Pleroma.Web.Endpoint,
|
||||
Keyword.merge(endpoint_conf, http: [port: 4012], url: [port: 4012], server: true)}
|
||||
]
|
||||
})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Spawns a configured map of federated nodes.
|
||||
|
||||
See `Pleroma.Cluster` module documentation for details.
|
||||
"""
|
||||
def spawn_cluster(node_configs) do
|
||||
# Turn node into a distributed node with the given long name
|
||||
:net_kernel.start([:"primary@127.0.0.1"])
|
||||
|
||||
# Allow spawned nodes to fetch all code from this node
|
||||
{:ok, _} = :erl_boot_server.start([])
|
||||
allow_boot("127.0.0.1")
|
||||
|
||||
silence_logger_warnings(fn ->
|
||||
node_configs
|
||||
|> Enum.map(&Task.async(fn -> start_slave(&1) end))
|
||||
|> Enum.map(&Task.await(&1, 60_000))
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Executes block of code again remote node.
|
||||
|
||||
See `Pleroma.Cluster` module documentation for details.
|
||||
"""
|
||||
defmacro within(node, do: block) do
|
||||
quote do
|
||||
rpc(unquote(node), unquote(__MODULE__), :eval_quoted, [
|
||||
unquote(Macro.escape(block)),
|
||||
binding()
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def eval_quoted(block, binding) do
|
||||
{result, _binding} = Code.eval_quoted(block, binding, __ENV__)
|
||||
result
|
||||
end
|
||||
|
||||
defp start_slave({node_host, override_configs}) do
|
||||
log(node_host, "booting federated VM")
|
||||
{:ok, node} = :slave.start(~c"127.0.0.1", node_name(node_host), vm_args())
|
||||
add_code_paths(node)
|
||||
load_apps_and_transfer_configuration(node, override_configs)
|
||||
ensure_apps_started(node)
|
||||
{:ok, node}
|
||||
end
|
||||
|
||||
def rpc(node, module, function, args) do
|
||||
:rpc.block_call(node, module, function, args)
|
||||
end
|
||||
|
||||
defp vm_args do
|
||||
~c"-loader inet -hosts 127.0.0.1 -setcookie #{:erlang.get_cookie()}"
|
||||
end
|
||||
|
||||
defp allow_boot(host) do
|
||||
{:ok, ipv4} = :inet.parse_ipv4_address(~c"#{host}")
|
||||
:ok = :erl_boot_server.add_slave(ipv4)
|
||||
end
|
||||
|
||||
defp add_code_paths(node) do
|
||||
rpc(node, :code, :add_paths, [:code.get_path()])
|
||||
end
|
||||
|
||||
defp load_apps_and_transfer_configuration(node, override_configs) do
|
||||
Enum.each(Application.loaded_applications(), fn {app_name, _, _} ->
|
||||
app_name
|
||||
|> Application.get_all_env()
|
||||
|> Enum.each(fn {key, primary_config} ->
|
||||
rpc(node, Application, :put_env, [app_name, key, primary_config, [persistent: true]])
|
||||
end)
|
||||
end)
|
||||
|
||||
Enum.each(override_configs, fn {app_name, key, val} ->
|
||||
rpc(node, Application, :put_env, [app_name, key, val, [persistent: true]])
|
||||
end)
|
||||
end
|
||||
|
||||
defp log(node, msg), do: IO.puts("[#{node}] #{msg}")
|
||||
|
||||
defp ensure_apps_started(node) do
|
||||
loaded_names = Enum.map(Application.loaded_applications(), fn {name, _, _} -> name end)
|
||||
app_names = @extra_apps ++ (loaded_names -- @extra_apps)
|
||||
|
||||
rpc(node, Application, :ensure_all_started, [:mix])
|
||||
rpc(node, Mix, :env, [Mix.env()])
|
||||
rpc(node, __MODULE__, :prepare_database, [])
|
||||
|
||||
log(node, "starting application")
|
||||
|
||||
Enum.reduce(app_names, MapSet.new(), fn app, loaded ->
|
||||
if Enum.member?(loaded, app) do
|
||||
loaded
|
||||
else
|
||||
{:ok, started} = rpc(node, Application, :ensure_all_started, [app])
|
||||
MapSet.union(loaded, MapSet.new(started))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def prepare_database do
|
||||
log(node(), "preparing database")
|
||||
repo_config = Application.get_env(:pleroma, Pleroma.Repo)
|
||||
repo_config[:adapter].storage_down(repo_config)
|
||||
repo_config[:adapter].storage_up(repo_config)
|
||||
|
||||
{:ok, _, _} =
|
||||
Ecto.Migrator.with_repo(Pleroma.Repo, fn repo ->
|
||||
Ecto.Migrator.run(repo, :up, log: false, all: true)
|
||||
end)
|
||||
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||
end
|
||||
|
||||
defp silence_logger_warnings(func) do
|
||||
prev_level = Logger.level()
|
||||
Logger.configure(level: :error)
|
||||
res = func.()
|
||||
Logger.configure(level: prev_level)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
defp node_name(node_host) do
|
||||
node_host
|
||||
|> to_string()
|
||||
|> String.split("@")
|
||||
|> Enum.at(0)
|
||||
|> String.to_atom()
|
||||
end
|
||||
end
|
|
@ -31,7 +31,6 @@ defmodule Pleroma.Factory do
|
|||
nickname: sequence(:nickname, &"nick#{&1}"),
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||
bio: sequence(:bio, &"Tester Number #{&1}"),
|
||||
info: %{},
|
||||
last_digest_emailed_at: NaiveDateTime.utc_now()
|
||||
}
|
||||
|
||||
|
|
|
@ -1035,6 +1035,22 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:8080/followers/fuser3", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/users_mock/friendica_followers.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:8080/following/fuser3", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/users_mock/friendica_following.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
|
||||
ExUnit.start(exclude: os_exclude)
|
||||
ExUnit.start(exclude: [:federated | os_exclude])
|
||||
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
||||
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||
|
|
|
@ -15,6 +15,14 @@ defmodule Pleroma.UserSearchTest do
|
|||
end
|
||||
|
||||
describe "User.search" do
|
||||
test "excluded invisible users from results" do
|
||||
user = insert(:user, %{nickname: "john t1000"})
|
||||
insert(:user, %{invisible: true, nickname: "john t800"})
|
||||
|
||||
[found_user] = User.search("john")
|
||||
assert found_user.id == user.id
|
||||
end
|
||||
|
||||
test "accepts limit parameter" do
|
||||
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
|
||||
assert length(User.search("john", limit: 3)) == 3
|
||||
|
|
|
@ -25,6 +25,25 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
clear_config([:instance, :account_activation_required])
|
||||
|
||||
describe "service actors" do
|
||||
test "returns invisible actor" do
|
||||
uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test"
|
||||
followers_uri = "#{uri}/followers"
|
||||
user = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
|
||||
|
||||
assert %User{
|
||||
nickname: "internal.fetch-test",
|
||||
invisible: true,
|
||||
local: true,
|
||||
ap_id: ^uri,
|
||||
follower_address: ^followers_uri
|
||||
} = user
|
||||
|
||||
user2 = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
|
||||
assert user.id == user2.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "when tags are nil" do
|
||||
test "tagging a user" do
|
||||
user = insert(:user, %{tags: nil})
|
||||
|
@ -148,9 +167,10 @@ defmodule Pleroma.UserTest do
|
|||
{:ok, user} = User.follow(user, followed)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
followed = User.get_cached_by_ap_id(followed.ap_id)
|
||||
|
||||
assert followed.follower_count == 1
|
||||
assert user.following_count == 1
|
||||
|
||||
assert User.ap_followers(followed) in User.following(user)
|
||||
end
|
||||
|
@ -347,18 +367,6 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||
end
|
||||
|
||||
test "it ensures info is not nil" do
|
||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||
|
||||
assert changeset.valid?
|
||||
|
||||
{:ok, user} =
|
||||
changeset
|
||||
|> Repo.insert()
|
||||
|
||||
refute is_nil(user.info)
|
||||
end
|
||||
end
|
||||
|
||||
describe "user registration, with :account_activation_required" do
|
||||
|
@ -412,8 +420,7 @@ defmodule Pleroma.UserTest do
|
|||
:user,
|
||||
local: false,
|
||||
nickname: "admin@mastodon.example.org",
|
||||
ap_id: ap_id,
|
||||
info: %{}
|
||||
ap_id: ap_id
|
||||
)
|
||||
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
|
@ -474,8 +481,7 @@ defmodule Pleroma.UserTest do
|
|||
local: false,
|
||||
nickname: "admin@mastodon.example.org",
|
||||
ap_id: "http://mastodon.example.org/users/admin",
|
||||
last_refreshed_at: a_week_ago,
|
||||
info: %{}
|
||||
last_refreshed_at: a_week_ago
|
||||
)
|
||||
|
||||
assert orig_user.last_refreshed_at == a_week_ago
|
||||
|
@ -516,7 +522,6 @@ defmodule Pleroma.UserTest do
|
|||
name: "Someone",
|
||||
nickname: "a@b.de",
|
||||
ap_id: "http...",
|
||||
info: %{some: "info"},
|
||||
avatar: %{some: "avatar"}
|
||||
}
|
||||
|
||||
|
@ -941,9 +946,9 @@ defmodule Pleroma.UserTest do
|
|||
{:ok, user} = User.follow(user, user2)
|
||||
{:ok, _user} = User.deactivate(user)
|
||||
|
||||
info = User.get_cached_user_info(user2)
|
||||
user2 = User.get_cached_by_id(user2.id)
|
||||
|
||||
assert info.follower_count == 0
|
||||
assert user2.follower_count == 0
|
||||
assert [] = User.get_followers(user2)
|
||||
end
|
||||
|
||||
|
@ -952,13 +957,15 @@ defmodule Pleroma.UserTest do
|
|||
user2 = insert(:user)
|
||||
|
||||
{:ok, user2} = User.follow(user2, user)
|
||||
assert user2.following_count == 1
|
||||
assert User.following_count(user2) == 1
|
||||
|
||||
{:ok, _user} = User.deactivate(user)
|
||||
|
||||
info = User.get_cached_user_info(user2)
|
||||
user2 = User.get_cached_by_id(user2.id)
|
||||
|
||||
assert info.following_count == 0
|
||||
assert refresh_record(user2).following_count == 0
|
||||
assert user2.following_count == 0
|
||||
assert User.following_count(user2) == 0
|
||||
assert [] = User.get_friends(user2)
|
||||
end
|
||||
|
@ -1121,8 +1128,7 @@ defmodule Pleroma.UserTest do
|
|||
ap_id: user.ap_id,
|
||||
name: user.name,
|
||||
nickname: user.nickname,
|
||||
bio: String.duplicate("h", current_max_length + 1),
|
||||
info: %{}
|
||||
bio: String.duplicate("h", current_max_length + 1)
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
|
@ -1135,8 +1141,7 @@ defmodule Pleroma.UserTest do
|
|||
data = %{
|
||||
ap_id: user.ap_id,
|
||||
name: String.duplicate("h", current_max_length + 1),
|
||||
nickname: user.nickname,
|
||||
info: %{}
|
||||
nickname: user.nickname
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
|
@ -1160,13 +1165,12 @@ defmodule Pleroma.UserTest do
|
|||
describe "caching" do
|
||||
test "invalidate_cache works" do
|
||||
user = insert(:user)
|
||||
_user_info = User.get_cached_user_info(user)
|
||||
|
||||
User.set_cache(user)
|
||||
User.invalidate_cache(user)
|
||||
|
||||
{:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
|
||||
{:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
|
||||
{:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
|
||||
end
|
||||
|
||||
test "User.delete() plugs any possible zombie objects" do
|
||||
|
@ -1322,7 +1326,7 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
{:ok, user} = User.block(user, follower)
|
||||
|
||||
assert User.user_info(user).follower_count == 2
|
||||
assert user.follower_count == 2
|
||||
end
|
||||
|
||||
describe "list_inactive_users_query/1" do
|
||||
|
@ -1499,51 +1503,6 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "set_info_cache/2" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, user: user}
|
||||
end
|
||||
|
||||
test "update from args", %{user: user} do
|
||||
User.set_info_cache(user, %{following_count: 15, follower_count: 18})
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
|
||||
assert followers == 18
|
||||
assert following == 15
|
||||
end
|
||||
|
||||
test "without args", %{user: user} do
|
||||
User.set_info_cache(user, %{})
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "user_info/2" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, user: user}
|
||||
end
|
||||
|
||||
test "update from args", %{user: user} do
|
||||
%{follower_count: followers, following_count: following} =
|
||||
User.user_info(user, %{following_count: 15, follower_count: 18})
|
||||
|
||||
assert followers == 18
|
||||
assert following == 15
|
||||
end
|
||||
|
||||
test "without args", %{user: user} do
|
||||
%{follower_count: followers, following_count: following} = User.user_info(user)
|
||||
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "is_internal_user?/1" do
|
||||
test "non-internal user returns false" do
|
||||
user = insert(:user)
|
||||
|
@ -1600,14 +1559,14 @@ defmodule Pleroma.UserTest do
|
|||
ap_enabled: true
|
||||
)
|
||||
|
||||
assert User.user_info(other_user).following_count == 0
|
||||
assert User.user_info(other_user).follower_count == 0
|
||||
assert other_user.following_count == 0
|
||||
assert other_user.follower_count == 0
|
||||
|
||||
{:ok, user} = Pleroma.User.follow(user, other_user)
|
||||
other_user = Pleroma.User.get_by_id(other_user.id)
|
||||
|
||||
assert User.user_info(user).following_count == 1
|
||||
assert User.user_info(other_user).follower_count == 1
|
||||
assert user.following_count == 1
|
||||
assert other_user.follower_count == 1
|
||||
end
|
||||
|
||||
test "syncronizes the counters with the remote instance for the followed when enabled" do
|
||||
|
@ -1623,14 +1582,14 @@ defmodule Pleroma.UserTest do
|
|||
ap_enabled: true
|
||||
)
|
||||
|
||||
assert User.user_info(other_user).following_count == 0
|
||||
assert User.user_info(other_user).follower_count == 0
|
||||
assert other_user.following_count == 0
|
||||
assert other_user.follower_count == 0
|
||||
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||
{:ok, _user} = User.follow(user, other_user)
|
||||
other_user = User.get_by_id(other_user.id)
|
||||
|
||||
assert User.user_info(other_user).follower_count == 437
|
||||
assert other_user.follower_count == 437
|
||||
end
|
||||
|
||||
test "syncronizes the counters with the remote instance for the follower when enabled" do
|
||||
|
@ -1646,13 +1605,13 @@ defmodule Pleroma.UserTest do
|
|||
ap_enabled: true
|
||||
)
|
||||
|
||||
assert User.user_info(other_user).following_count == 0
|
||||
assert User.user_info(other_user).follower_count == 0
|
||||
assert other_user.following_count == 0
|
||||
assert other_user.follower_count == 0
|
||||
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||
{:ok, other_user} = User.follow(other_user, user)
|
||||
|
||||
assert User.user_info(other_user).following_count == 152
|
||||
assert other_user.following_count == 152
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -110,6 +110,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
||||
end
|
||||
|
||||
test "it returns 404 for remote users", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user, local: false, nickname: "remoteuser@example.com")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> get("/users/#{user.nickname}.json")
|
||||
|
||||
assert json_response(conn, 404)
|
||||
end
|
||||
end
|
||||
|
||||
describe "/object/:uuid" do
|
||||
|
|
|
@ -1554,6 +1554,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert follow_info.hide_followers == false
|
||||
assert follow_info.hide_follows == true
|
||||
end
|
||||
|
||||
test "detects hidden follows/followers for friendica" do
|
||||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:8080/followers/fuser3",
|
||||
following_address: "http://localhost:8080/following/fuser3"
|
||||
)
|
||||
|
||||
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
assert follow_info.hide_followers == true
|
||||
assert follow_info.follower_count == 296
|
||||
assert follow_info.following_count == 32
|
||||
assert follow_info.hide_follows == true
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch_favourites/3" do
|
||||
|
|
|
@ -39,6 +39,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert activity == returned_activity
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it fetches replied-to activities if we don't have them" do
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||
|
@ -533,6 +534,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert object.data["content"] == "this is a private toot"
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||
data =
|
||||
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||
|
@ -814,6 +816,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert Activity.get_by_id(activity.id)
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it works for incoming user deletes" do
|
||||
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
|
||||
|
||||
|
@ -1749,6 +1752,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert modified_object["inReplyToAtomUri"] == ""
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "returns modified object when allowed incoming reply", %{data: data} do
|
||||
object_with_reply =
|
||||
Map.put(
|
||||
|
@ -1868,6 +1872,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
end) =~ "Unsupported URI scheme"
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "returns {:ok, %Object{}} for success case" do
|
||||
assert {:ok, %Object{}} =
|
||||
Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
|
||||
|
|
|
@ -225,7 +225,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"roles" => %{"admin" => false, "moderator" => false},
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
|
||||
assert expected == json_response(conn, 200)
|
||||
|
@ -634,7 +635,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
},
|
||||
%{
|
||||
"deactivated" => user.deactivated,
|
||||
|
@ -644,7 +646,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => false,
|
||||
"tags" => ["foo", "bar"],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
@ -685,7 +688,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -709,7 +713,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -733,7 +738,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -757,7 +763,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -781,7 +788,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -805,7 +813,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -824,7 +833,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname)
|
||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -853,7 +863,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -880,7 +891,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
},
|
||||
%{
|
||||
"deactivated" => admin.deactivated,
|
||||
|
@ -890,7 +902,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
},
|
||||
%{
|
||||
"deactivated" => false,
|
||||
|
@ -900,7 +913,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"roles" => %{"admin" => true, "moderator" => false},
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname)
|
||||
"display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
@ -929,7 +943,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => admin.local,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
},
|
||||
%{
|
||||
"deactivated" => false,
|
||||
|
@ -939,7 +954,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => second_admin.local,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname)
|
||||
"display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
@ -970,7 +986,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => moderator.local,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(moderator.name || moderator.nickname)
|
||||
"display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -994,7 +1011,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => user1.local,
|
||||
"tags" => ["first"],
|
||||
"avatar" => User.avatar_url(user1) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user1.name || user1.nickname)
|
||||
"display_name" => HTML.strip_tags(user1.name || user1.nickname),
|
||||
"confirmation_pending" => false
|
||||
},
|
||||
%{
|
||||
"deactivated" => false,
|
||||
|
@ -1004,7 +1022,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => user2.local,
|
||||
"tags" => ["second"],
|
||||
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname)
|
||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
@ -1040,7 +1059,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => user.local,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1066,7 +1086,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1135,7 +1156,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"local" => true,
|
||||
"tags" => [],
|
||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||
"confirmation_pending" => false
|
||||
}
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
@ -1901,6 +1923,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "create new config setting in db", %{conn: conn} do
|
||||
conn =
|
||||
post(conn, "/api/pleroma/admin/config", %{
|
||||
|
@ -2839,6 +2862,105 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
|
||||
end
|
||||
end
|
||||
|
||||
describe "instances" do
|
||||
test "GET /instances/:instance/statuses" do
|
||||
admin = insert(:user, is_admin: true)
|
||||
user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
||||
user2 = insert(:user, local: false, nickname: "test@test.com")
|
||||
insert_pair(:note_activity, user: user)
|
||||
insert(:note_activity, user: user2)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/instances/archae.me/statuses")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 2
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/instances/test.com/statuses")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 1
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/instances/nonexistent.com/statuses")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH /confirm_email" do
|
||||
setup %{conn: conn} do
|
||||
admin = insert(:user, is_admin: true)
|
||||
|
||||
%{conn: assign(conn, :user, admin), admin: admin}
|
||||
end
|
||||
|
||||
test "it confirms emails of two users", %{admin: admin} do
|
||||
[first_user, second_user] = insert_pair(:user, confirmation_pending: true)
|
||||
|
||||
assert first_user.confirmation_pending == true
|
||||
assert second_user.confirmation_pending == true
|
||||
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> patch("/api/pleroma/admin/users/confirm_email", %{
|
||||
nicknames: [
|
||||
first_user.nickname,
|
||||
second_user.nickname
|
||||
]
|
||||
})
|
||||
|
||||
assert first_user.confirmation_pending == true
|
||||
assert second_user.confirmation_pending == true
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
|
||||
second_user.nickname
|
||||
}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH /resend_confirmation_email" do
|
||||
setup %{conn: conn} do
|
||||
admin = insert(:user, is_admin: true)
|
||||
|
||||
%{conn: assign(conn, :user, admin), admin: admin}
|
||||
end
|
||||
|
||||
test "it resend emails for two users", %{admin: admin} do
|
||||
[first_user, second_user] = insert_pair(:user, confirmation_pending: true)
|
||||
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> patch("/api/pleroma/admin/users/resend_confirmation_email", %{
|
||||
nicknames: [
|
||||
first_user.nickname,
|
||||
second_user.nickname
|
||||
]
|
||||
})
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
|
||||
second_user.nickname
|
||||
}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Needed for testing
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
|
@ -118,6 +119,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
refute acc_one == acc_two
|
||||
assert acc_two == acc_three
|
||||
end
|
||||
|
||||
test "returns 404 when user is invisible", %{conn: conn} do
|
||||
user = insert(:user, %{invisible: true})
|
||||
|
||||
resp =
|
||||
conn
|
||||
|> get("/api/v1/accounts/#{user.nickname}")
|
||||
|> json_response(404)
|
||||
|
||||
assert %{"error" => "Can't find user"} = resp
|
||||
end
|
||||
|
||||
test "returns 404 for internal.fetch actor", %{conn: conn} do
|
||||
%User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
|
||||
|
||||
resp =
|
||||
conn
|
||||
|> get("/api/v1/accounts/internal.fetch")
|
||||
|> json_response(404)
|
||||
|
||||
assert %{"error" => "Can't find user"} = resp
|
||||
end
|
||||
end
|
||||
|
||||
describe "user timelines" do
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Web.MastodonAPI.FilterView
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ScheduledActivity
|
||||
|
|
|
@ -350,7 +350,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
}
|
||||
}
|
||||
|
||||
assert expected == AccountView.render("show.json", %{user: user, for: other_user})
|
||||
assert expected ==
|
||||
AccountView.render("show.json", %{user: refresh_record(user), for: other_user})
|
||||
end
|
||||
|
||||
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
|
||||
|
@ -374,6 +375,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
refute result.display_name == "<marquee> username </marquee>"
|
||||
end
|
||||
|
||||
test "never display nil user follow counts" do
|
||||
user = insert(:user, following_count: 0, follower_count: 0)
|
||||
result = AccountView.render("show.json", %{user: user})
|
||||
|
||||
assert result.following_count == 0
|
||||
assert result.followers_count == 0
|
||||
end
|
||||
|
||||
describe "hiding follows/following" do
|
||||
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
|
||||
user =
|
||||
|
|
|
@ -35,23 +35,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
||||
end
|
||||
|
||||
test "500s when user not found", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||
User.invalidate_cache(user)
|
||||
Pleroma.Repo.delete(user)
|
||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
|
||||
url = "/objects/#{uuid}"
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/xml")
|
||||
|> get(url)
|
||||
|
||||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
||||
end
|
||||
|
||||
test "404s on private objects", %{conn: conn} do
|
||||
note_activity = insert(:direct_note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
|
@ -82,21 +65,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
||||
end
|
||||
|
||||
test "505s when user not found", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||
User.invalidate_cache(user)
|
||||
Pleroma.Repo.delete(user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "text/html")
|
||||
|> get("/activities/#{uuid}")
|
||||
|
||||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
||||
end
|
||||
|
||||
test "404s on private activities", %{conn: conn} do
|
||||
note_activity = insert(:direct_note_activity)
|
||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
||||
|
@ -127,21 +95,28 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
end
|
||||
|
||||
describe "GET notice/2" do
|
||||
test "gets a notice in xml format", %{conn: conn} do
|
||||
test "redirects to a proper object URL when json requested and the object is local", %{
|
||||
conn: conn
|
||||
} do
|
||||
note_activity = insert(:note_activity)
|
||||
expected_redirect_url = Object.normalize(note_activity).data["id"]
|
||||
|
||||
conn
|
||||
|> get("/notice/#{note_activity.id}")
|
||||
|> response(200)
|
||||
redirect_url =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/notice/#{note_activity.id}")
|
||||
|> redirected_to()
|
||||
|
||||
assert redirect_url == expected_redirect_url
|
||||
end
|
||||
|
||||
test "gets a notice in AS2 format", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
test "returns a 404 on remote notice when json requested", %{conn: conn} do
|
||||
note_activity = insert(:note_activity, local: false)
|
||||
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/notice/#{note_activity.id}")
|
||||
|> json_response(200)
|
||||
|> response(404)
|
||||
end
|
||||
|
||||
test "500s when actor not found", %{conn: conn} do
|
||||
|
@ -157,32 +132,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
||||
end
|
||||
|
||||
test "only gets a notice in AS2 format for Create messages", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
url = "/notice/#{note_activity.id}"
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get(url)
|
||||
|
||||
assert json_response(conn, 200)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
|
||||
url = "/notice/#{like_activity.id}"
|
||||
|
||||
assert like_activity.data["type"] == "Like"
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get(url)
|
||||
|
||||
assert response(conn, 404)
|
||||
end
|
||||
|
||||
test "render html for redirect for html format", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
alias Pleroma.Web.Streamer.StreamerSocket
|
||||
alias Pleroma.Web.Streamer.Worker
|
||||
|
||||
@moduletag needs_streamer: true
|
||||
@moduletag needs_streamer: true, capture_log: true
|
||||
clear_config_all([:instance, :skip_thread_containment])
|
||||
|
||||
describe "user streams" do
|
||||
|
|
Loading…
Reference in a new issue