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 @@ def query_timelines(user) 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 @@ defp generate_user_data(i) do
|
|||
password_hash:
|
||||
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
||||
bio: "Tester Number #{i}",
|
||||
info: %{},
|
||||
local: remote
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,10 @@ def run(args) 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 @@
|
|||
|
||||
# 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 :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 :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 @@
|
|||
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 @@ def run(["sign_out", nickname]) 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 @@ def restrict_deactivated_users(query) 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 @@ def named_version, do: @name <> " " <> @version
|
|||
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 @@ def following(%User{} = user) 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 @@ def extract_first_external_url(object, content) 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 @@ def get_log_entry_message(%ModerationLog{
|
|||
"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 @@ def get_by_ap_id(ap_id) 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 @@ def update_data(%Object{data: data} = object, attrs \\ %{}) 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 @@ defp reinject_object(struct, data) 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 @@ def secret_token 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 @@ def auth_active?(%User{}), do: true
|
|||
|
||||
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 @@ def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
|
|||
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 @@ def set_follow_state_cache(user_ap_id, target_ap_id, state) 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 @@ def remote_user_creation(params) 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 @@ def try_send_confirmation_email(%User{} = 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 @@ def maybe_direct_follow(%User{} = follower, %User{} = followed) 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 @@ def follow(%User{} = follower, %User{} = followed, state \\ "accept") 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 @@ def unfollow(%User{} = follower, %User{} = followed) 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 @@ def set_cache({:error, err}), do: {:error, err}
|
|||
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 @@ def update_and_set_cache(changeset) 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 @@ def get_by_nickname_or_email(nickname_or_email) 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 @@ def update_follower_count(%User{} = 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 @@ def maybe_update_following_count(%User{local: false} = 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 @@ def deactivate(users, status) when is_list(users) 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 @@ def external_users_query 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 @@ def get_or_fetch_by_ap_id(ap_id) 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 @@ def toggle_confirmation(%User{} = 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 @@ defp search_query(query_string, for_user, following) 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 @@ defp trigram_rank(query, query_string) 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 @@ def fetch_user_activities(user, reading_user, params \\ %{}) 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 @@ defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) 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 @@ def fetch_activities_query(recipients, opts \\ %{}) 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 @@ defp maybe_update_follow_information(data) 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 @@ defp collection_private(data) 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 @@ def relay_active?(conn, _) 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 @@ def user(conn, %{"nickname" => nickname}) do
|
|||
|> render("user.json", %{user: user})
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
%{local: false} -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ def get_actor 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 @@ def update_report_state(_, _), do: {:error, "Unsupported state"}
|
|||
|
||||
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 @@ def user_show(conn, %{"nickname" => nickname}) 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 @@ def list_users(conn, params) 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 @@ def list_users(conn, params) 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 @@ def reload_emoji(conn, _params) 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 @@ def render("show.json", %{user: user}) 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 @@ def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
|||
@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 @@ defp do_render("show.json", %{user: user} = opts) 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 @@ defp do_render("show.json", %{user: user} = opts) 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 @@ defp do_render("show.json", %{user: user} = opts) 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 @@ defp maybe_put_settings(
|
|||
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 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
|||
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 @@ def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) 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 @@ def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
|||
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 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) 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 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) 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 @@ def notice_player(conn, %{"id" => id}) 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 @@ def perform(
|
|||
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 @@ def perform(
|
|||
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 @@ defp deps 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 @@ defp version(version) 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 @@ test "skips microformats hashtags" 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 @@ test "don't send pleroma user agent" 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 @@ test "does nothing if a user is assigned", %{conn: conn} 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 @@ def user_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 @@ def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) 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 @@ test "follow takes a user and another user" 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 @@ test "it sets the password_hash and ap_id" 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 @@ test "gets an existing user by ap_id" 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 @@ test "updates an existing user, if stale" 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 @@ test "returns an ap_followers link for a user" do
|
|||
name: "Someone",
|
||||
nickname: "a@b.de",
|
||||
ap_id: "http...",
|
||||
info: %{some: "info"},
|
||||
avatar: %{some: "avatar"}
|
||||
}
|
||||
|
||||
|
@ -941,9 +946,9 @@ test "hide a user from followers" 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 @@ test "hide a user from friends" 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 @@ test "with an overly long bio" 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 @@ test "with an overly long display name" 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 @@ test "html_filter_policy returns TwitterText scrubber when rich-text is disabled
|
|||
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 @@ test "follower count is updated when a follower is blocked" 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 @@ test "external_users/1 external active users with limit", %{user1: user1, user2:
|
|||
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 @@ test "updates the counters normally on following/getting a follow when disabled"
|
|||
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 @@ test "syncronizes the counters with the remote instance for the followed when en
|
|||
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 @@ test "syncronizes the counters with the remote instance for the follower when en
|
|||
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 @@ test "it returns a json representation of the user with accept application/ld+js
|
|||
|
||||
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 @@ test "detects hidden follows" 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 @@ test "it ignores an incoming notice if we already have it" 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 @@ test "it works for incoming announces with an inlined activity" 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 @@ test "it fails for incoming deletes with spoofed origin" 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 @@ test "returns object with inReplyToAtomUri when denied incoming reply", %{data:
|
|||
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 @@ test "returns nil when cannot normalize object" 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 @@ test "Show", %{conn: conn} 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 @@ test "renders users array for the first page", %{conn: conn, admin: admin} 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 @@ test "renders users array for the first page", %{conn: conn, admin: admin} 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 @@ test "regular search", %{conn: conn} 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 @@ test "search by domain", %{conn: conn} 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 @@ test "search by full nickname", %{conn: conn} 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 @@ test "search by display name", %{conn: conn} 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 @@ test "search by email", %{conn: conn} 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 @@ test "regular search with page size", %{conn: conn} 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 @@ test "regular search with page size", %{conn: conn} 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 @@ test "only local users" 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 @@ test "only local users with no query", %{admin: old_admin} 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 @@ test "only local users with no query", %{admin: old_admin} 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 @@ test "only local users with no query", %{admin: old_admin} 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 @@ test "load only admins", %{conn: conn, admin: admin} 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 @@ test "load only admins", %{conn: conn, admin: admin} 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 @@ test "load only moderators", %{conn: conn} 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 @@ test "load users with tags list", %{conn: conn} 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 @@ test "load users with tags list", %{conn: conn} 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 @@ test "it works with multiple filters" 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 @@ test "it omits relay user", %{admin: admin} 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 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" 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 @@ test "with settings in db", %{conn: conn} 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 @@ test "DELETE /relay", %{admin: admin} 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 @@ test "accounts fetches correct account for nicknames beginning with numbers", %{
|
|||
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 @@ test "represent an embedded relationship" 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 @@ test "sanitizes display names" 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 @@ test "redirects to /notice/id for html format", %{conn: conn} 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 @@ test "redirects to /notice/id for html format", %{conn: conn} 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 @@ test "gets an activity in AS2 format", %{conn: conn} 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 @@ test "500s when actor not found", %{conn: conn} 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