Merge remote-tracking branch 'origin/develop' into paradedb-search-backend
This commit is contained in:
commit
2cc7b8356c
118 changed files with 1639 additions and 851 deletions
1
changelog.d/bookmark-folders.ignore
Normal file
1
changelog.d/bookmark-folders.ignore
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Various bookmark folders-related improvements
|
||||||
1
changelog.d/boost-visibilities.add
Normal file
1
changelog.d/boost-visibilities.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Allow fine-grained announce visibilities
|
||||||
0
changelog.d/credo-aliases-sort-fixes.skip
Normal file
0
changelog.d/credo-aliases-sort-fixes.skip
Normal file
1
changelog.d/elixir-1.19-cherrypicks.change
Normal file
1
changelog.d/elixir-1.19-cherrypicks.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
No-op code correctness improvements detected by Elixir 1.19 compiler
|
||||||
1
changelog.d/exclusive-lists.add
Normal file
1
changelog.d/exclusive-lists.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Support lists `exclusive` param
|
||||||
1
changelog.d/gopher-genserver-crash-on-boot.fix
Normal file
1
changelog.d/gopher-genserver-crash-on-boot.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
|
||||||
1
changelog.d/hubzilla-alsoknownas.fix
Normal file
1
changelog.d/hubzilla-alsoknownas.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix fetching Hubzilla Actors with alsoKnownAs as string
|
||||||
1
changelog.d/live-dashboard-redirect.fix
Normal file
1
changelog.d/live-dashboard-redirect.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix /phoenix/live_dashboard redirect not working when user added a path segment
|
||||||
1
changelog.d/missing-static-file.fix
Normal file
1
changelog.d/missing-static-file.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix 404 error codes for missing static files
|
||||||
1
changelog.d/old-migrations.fix
Normal file
1
changelog.d/old-migrations.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Correct old migrations for expiring activities and user access tokens.
|
||||||
1
changelog.d/rate-limiter-hardening.fix
Normal file
1
changelog.d/rate-limiter-hardening.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Stop the rate limiter from crashing when run with wrong settings.
|
||||||
1
changelog.d/restore-embeds.fix
Normal file
1
changelog.d/restore-embeds.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Restore embeds route
|
||||||
1
changelog.d/reverseproxy-recursive-redirect.fix
Normal file
1
changelog.d/reverseproxy-recursive-redirect.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ReverseProxy: Recursively follow redirects until redirect_limit is reached
|
||||||
1
changelog.d/search-indexing.change
Normal file
1
changelog.d/search-indexing.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Filter indexable activities before inserting indexing jobs into the queue.
|
||||||
0
changelog.d/twitter-api.skip
Normal file
0
changelog.d/twitter-api.skip
Normal file
1
changelog.d/user-view.ignore
Normal file
1
changelog.d/user-view.ignore
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Avoid code duplication in UserView
|
||||||
|
|
@ -74,7 +74,7 @@ Pleroma does not process remote images and therefore cannot include fields such
|
||||||
|
|
||||||
The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID.
|
The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID.
|
||||||
|
|
||||||
The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID.
|
The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID. Bookmarking an already bookmarked post will update the folder association, or remove it if `folder_id` is omitted or `null`.
|
||||||
|
|
||||||
## Accounts
|
## Accounts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Bookmark do
|
||||||
schema "bookmarks" do
|
schema "bookmarks" do
|
||||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||||
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.CompatType)
|
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.Type)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Bookmark do
|
||||||
|> validate_required([:user_id, :activity_id])
|
|> validate_required([:user_id, :activity_id])
|
||||||
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|
||||||
|> Repo.insert(
|
|> Repo.insert(
|
||||||
on_conflict: [set: [folder_id: folder_id]],
|
on_conflict: [set: [folder_id: folder_id, updated_at: NaiveDateTime.utc_now()]],
|
||||||
conflict_target: [:user_id, :activity_id]
|
conflict_target: [:user_id, :activity_id]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -76,11 +76,4 @@ defmodule Pleroma.Bookmark do
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|> Repo.delete()
|
|> Repo.delete()
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_folder(bookmark, folder_id) do
|
|
||||||
bookmark
|
|
||||||
|> cast(%{folder_id: folder_id}, [:folder_id])
|
|
||||||
|> validate_required([:folder_id])
|
|
||||||
|> Repo.update()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.BookmarkFolder do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
|
@primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
|
||||||
|
|
||||||
schema "bookmark_folders" do
|
schema "bookmark_folders" do
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
alias __MODULE__
|
alias __MODULE__
|
||||||
|
alias Pleroma.EctoType.Config.RateLimit
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|
||||||
|> cast(params, [:key, :group, :value])
|
|> cast(params, [:key, :group, :value])
|
||||||
|> validate_required([:key, :group, :value])
|
|> validate_required([:key, :group, :value])
|
||||||
|> unique_constraint(:key, name: :config_group_key_index)
|
|> unique_constraint(:key, name: :config_group_key_index)
|
||||||
|
|> validate_rate_limit()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validate_rate_limit(changeset) do
|
||||||
|
group = get_field(changeset, :group)
|
||||||
|
key = get_field(changeset, :key)
|
||||||
|
|
||||||
|
if group == :pleroma and key == :rate_limit do
|
||||||
|
value = get_field(changeset, :value)
|
||||||
|
|
||||||
|
case normalize_rate_limit(value) do
|
||||||
|
{:ok, normalized_value} ->
|
||||||
|
put_change(changeset, :value, normalized_value)
|
||||||
|
|
||||||
|
{:error, {limiter_name, reason}} ->
|
||||||
|
add_error(
|
||||||
|
changeset,
|
||||||
|
:value,
|
||||||
|
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_rate_limit(nil), do: {:ok, nil}
|
||||||
|
|
||||||
|
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
|
||||||
|
|
||||||
|
defp normalize_rate_limit(value) when is_list(value) do
|
||||||
|
if Keyword.keyword?(value) do
|
||||||
|
value
|
||||||
|
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
|
||||||
|
case RateLimit.cast_with_error(limiter_value) do
|
||||||
|
{:ok, normalized_limiter_value} ->
|
||||||
|
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:halt, {:error, {limiter_name, reason}}}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, acc} -> {:ok, Enum.reverse(acc)}
|
||||||
|
{:error, _} = error -> error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:error, {:rate_limit, "must be a keyword list"}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
|
||||||
|
|
||||||
defp create(params) do
|
defp create(params) do
|
||||||
%ConfigDB{}
|
%ConfigDB{}
|
||||||
|> changeset(params)
|
|> changeset(params)
|
||||||
|
|
|
||||||
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.EctoType.Config.RateLimit do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Ecto.Type
|
||||||
|
|
||||||
|
@type t ::
|
||||||
|
nil
|
||||||
|
| {non_neg_integer(), non_neg_integer()}
|
||||||
|
| [{non_neg_integer(), non_neg_integer()}]
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def type, do: :term
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast(value) do
|
||||||
|
case cast_with_error(value) do
|
||||||
|
{:ok, normalized} -> {:ok, normalized}
|
||||||
|
{:error, _reason} -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def load(value), do: cast(value)
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def dump(value), do: cast(value)
|
||||||
|
|
||||||
|
@spec cast_with_error(term()) :: {:ok, t()} | {:error, String.t()}
|
||||||
|
def cast_with_error(nil), do: {:ok, nil}
|
||||||
|
|
||||||
|
def cast_with_error({scale, limit}) do
|
||||||
|
with {:ok, scale} <- parse_integer(scale, "scale"),
|
||||||
|
{:ok, limit} <- parse_integer(limit, "limit"),
|
||||||
|
true <- scale >= 1 and limit >= 1 do
|
||||||
|
{:ok, {scale, limit}}
|
||||||
|
else
|
||||||
|
false -> {:error, "scale and limit must be >= 1"}
|
||||||
|
{:error, reason} -> {:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_with_error([{_, _} = unauth, {_, _} = auth]) do
|
||||||
|
with {:ok, unauth} <- cast_with_error(unauth),
|
||||||
|
{:ok, auth} <- cast_with_error(auth) do
|
||||||
|
{:ok, [unauth, auth]}
|
||||||
|
else
|
||||||
|
{:error, reason} -> {:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_with_error(_),
|
||||||
|
do:
|
||||||
|
{:error, "must be a {scale, limit} tuple, a [{scale, limit}, {scale, limit}] list, or nil"}
|
||||||
|
|
||||||
|
defp parse_integer(value, _label) when is_integer(value), do: {:ok, value}
|
||||||
|
|
||||||
|
defp parse_integer(value, label) when is_binary(value) do
|
||||||
|
value = String.trim(value)
|
||||||
|
|
||||||
|
case Integer.parse(value) do
|
||||||
|
{number, ""} -> {:ok, number}
|
||||||
|
_ -> {:error, "#{label} must be an integer"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_integer(_value, label), do: {:error, "#{label} must be an integer"}
|
||||||
|
end
|
||||||
|
|
@ -21,10 +21,13 @@ defmodule Pleroma.Gopher.Server do
|
||||||
|
|
||||||
def init([ip, port]) do
|
def init([ip, port]) do
|
||||||
Logger.info("Starting gopher server on #{port}")
|
Logger.info("Starting gopher server on #{port}")
|
||||||
|
Process.flag(:trap_exit, true)
|
||||||
|
|
||||||
|
listener = :gopher
|
||||||
|
|
||||||
{:ok, _pid} =
|
{:ok, _pid} =
|
||||||
:ranch.start_listener(
|
:ranch.start_listener(
|
||||||
:gopher,
|
listener,
|
||||||
:ranch_tcp,
|
:ranch_tcp,
|
||||||
%{
|
%{
|
||||||
num_acceptors: 100,
|
num_acceptors: 100,
|
||||||
|
|
@ -35,7 +38,11 @@ defmodule Pleroma.Gopher.Server do
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, %{ip: ip, port: port}}
|
{:ok, %{ip: ip, port: port, listener: listener}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def terminate(_reason, state) do
|
||||||
|
:ranch.stop_listener(state.listener)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,14 @@ defmodule Pleroma.List do
|
||||||
field(:title, :string)
|
field(:title, :string)
|
||||||
field(:following, {:array, :string}, default: [])
|
field(:following, {:array, :string}, default: [])
|
||||||
field(:ap_id, :string)
|
field(:ap_id, :string)
|
||||||
|
field(:exclusive, :boolean, default: false)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
def title_changeset(list, attrs \\ %{}) do
|
def update_changeset(list, attrs \\ %{}) do
|
||||||
list
|
list
|
||||||
|> cast(attrs, [:title])
|
|> cast(attrs, [:title, :exclusive])
|
||||||
|> validate_required([:title])
|
|> validate_required([:title])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -91,14 +92,14 @@ defmodule Pleroma.List do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def rename(%Pleroma.List{} = list, title) do
|
def update(%Pleroma.List{} = list, params) do
|
||||||
list
|
list
|
||||||
|> title_changeset(%{title: title})
|
|> update_changeset(params)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(title, %User{} = creator) do
|
def create(params, %User{} = creator) do
|
||||||
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title})
|
changeset = update_changeset(%Pleroma.List{user_id: creator.id}, params)
|
||||||
|
|
||||||
if changeset.valid? do
|
if changeset.valid? do
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
|
|
@ -149,4 +150,14 @@ defmodule Pleroma.List do
|
||||||
end
|
end
|
||||||
|
|
||||||
def member?(_, _), do: false
|
def member?(_, _), do: false
|
||||||
|
|
||||||
|
def get_exclusive_list_members(%User{id: user_id}) do
|
||||||
|
Pleroma.List
|
||||||
|
|> where([l], l.user_id == ^user_id)
|
||||||
|
|> where([l], l.exclusive == true)
|
||||||
|
|> select([l], l.following)
|
||||||
|
|> Repo.all()
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.uniq()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ defmodule Pleroma.Marker do
|
||||||
|
|
||||||
defp get_marker(user, timeline) do
|
defp get_marker(user, timeline) do
|
||||||
case Repo.find_resource(get_query(user, timeline)) do
|
case Repo.find_resource(get_query(user, timeline)) do
|
||||||
{:ok, marker} -> %__MODULE__{marker | user: user}
|
{:ok, %__MODULE__{} = marker} -> %{marker | user: user}
|
||||||
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
|
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
|
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
|
||||||
settings =
|
%Settings{} =
|
||||||
|
settings =
|
||||||
changeset
|
changeset
|
||||||
|> Ecto.Changeset.apply_changes()
|
|> Ecto.Changeset.apply_changes()
|
||||||
|> MFA.fetch_settings()
|
|> MFA.fetch_settings()
|
||||||
|
|
@ -20,20 +21,20 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
|
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||||
totp_settings = %Settings.TOTP{settings.totp | confirmed: true}
|
totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true}
|
||||||
|
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
|
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_totp(%User{} = user, attrs) do
|
def setup_totp(%User{} = user, attrs) do
|
||||||
mfa_settings = MFA.fetch_settings(user)
|
%Settings{} = mfa_settings = MFA.fetch_settings(user)
|
||||||
|
|
||||||
totp_settings =
|
totp_settings =
|
||||||
%Settings.TOTP{}
|
%Settings.TOTP{}
|
||||||
|
|
@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
def cast_backup_codes(%User{} = user, codes) do
|
def cast_backup_codes(%User{} = user, codes) do
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{
|
|> put_change(%Settings{
|
||||||
user.multi_factor_authentication_settings
|
(%Settings{} = user.multi_factor_authentication_settings)
|
||||||
| backup_codes: codes
|
| backup_codes: codes
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
defmodule Pleroma.ReverseProxy.Client.Hackney do
|
defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
@behaviour Pleroma.ReverseProxy.Client
|
@behaviour Pleroma.ReverseProxy.Client
|
||||||
|
@redirect_limit 5
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
# In-app redirect handler to avoid Hackney redirect bugs:
|
# In-app redirect handler to avoid Hackney redirect bugs:
|
||||||
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
|
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
|
||||||
|
|
@ -31,26 +34,15 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
|
|
||||||
if opts[:follow_redirect] != false do
|
if opts[:follow_redirect] != false do
|
||||||
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
||||||
|
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
|
||||||
res = :hackney.request(method, url, headers, body, req_opts)
|
res = :hackney.request(method, url, headers, body, req_opts)
|
||||||
|
|
||||||
case res do
|
case res do
|
||||||
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
||||||
:hackney.request(
|
redirect(url, resp_headers, env, @redirect_limit)
|
||||||
method,
|
|
||||||
absolute_redirect_url(url, resp_headers),
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
req_opts
|
|
||||||
)
|
|
||||||
|
|
||||||
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
||||||
:hackney.request(
|
redirect(url, resp_headers, env, @redirect_limit)
|
||||||
method,
|
|
||||||
absolute_redirect_url(url, resp_headers),
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
req_opts
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
res
|
res
|
||||||
|
|
@ -71,4 +63,32 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def close(ref), do: :hackney.close(ref)
|
def close(ref), do: :hackney.close(ref)
|
||||||
|
|
||||||
|
defp redirect(url, resp_headers, env, limit) when limit == 0 do
|
||||||
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
|
|
||||||
|
Logger.debug(
|
||||||
|
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
|
||||||
|
)
|
||||||
|
|
||||||
|
:hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp redirect(url, resp_headers, env, limit) do
|
||||||
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
|
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
|
||||||
|
|
||||||
|
res = :hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
|
|
||||||
|
case res do
|
||||||
|
{:ok, code, new_resp_headers, _client} when code in @redirect_statuses ->
|
||||||
|
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||||
|
|
||||||
|
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
|
||||||
|
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
res
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,28 @@
|
||||||
defmodule Pleroma.Search do
|
defmodule Pleroma.Search do
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Workers.SearchIndexingWorker
|
alias Pleroma.Workers.SearchIndexingWorker
|
||||||
|
|
||||||
def add_to_index(%Pleroma.Activity{id: activity_id}) do
|
@spec add_to_index(Activity.t()) :: {:ok, Oban.Job.t() | :noop} | {:error, Oban.Job.changeset()}
|
||||||
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
def add_to_index(%Activity{id: activity_id, object: %Object{} = object} = activity) do
|
||||||
|> Oban.insert()
|
with {_, true} <- {:indexable, indexable?(activity)},
|
||||||
|
{_, "public"} <- {:visibility, Visibility.get_visibility(object)} do
|
||||||
|
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
||||||
|
|> Oban.insert()
|
||||||
|
else
|
||||||
|
_ -> {:ok, :noop}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_to_index(%Activity{id: activity_id}) do
|
||||||
|
case Activity.get_by_id_with_object(activity_id) do
|
||||||
|
%Activity{} = preloaded -> add_to_index(preloaded)
|
||||||
|
_ -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec remove_from_index(Object.t()) :: {:ok, Oban.Job.t()} | {:error, Oban.Job.changeset()}
|
||||||
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
||||||
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|
||||||
|> Oban.insert()
|
|> Oban.insert()
|
||||||
|
|
@ -20,4 +37,7 @@ defmodule Pleroma.Search do
|
||||||
search_module = Pleroma.Config.get([Pleroma.Search, :module])
|
search_module = Pleroma.Config.get([Pleroma.Search, :module])
|
||||||
search_module.healthcheck_endpoints()
|
search_module.healthcheck_endpoints()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp indexable?(%Activity{data: %{"type" => "Create"}}), do: true
|
||||||
|
defp indexable?(_), do: false
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Search.Meilisearch do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config.Getting, as: Config
|
alias Pleroma.Config.Getting, as: Config
|
||||||
|
alias Pleroma.Object
|
||||||
|
|
||||||
import Pleroma.Search.DatabaseSearch
|
import Pleroma.Search.DatabaseSearch
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
@ -156,28 +157,23 @@ defmodule Pleroma.Search.Meilisearch do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def add_to_index(activity) do
|
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||||
maybe_search_data = object_to_search_data(activity.object)
|
search_data = object_to_search_data(object)
|
||||||
|
|
||||||
if activity.data["type"] == "Create" and maybe_search_data do
|
result =
|
||||||
result =
|
meili_put(
|
||||||
meili_put(
|
"/indexes/objects/documents",
|
||||||
"/indexes/objects/documents",
|
[search_data]
|
||||||
[maybe_search_data]
|
)
|
||||||
)
|
|
||||||
|
|
||||||
with {:ok, %{"status" => "enqueued"}} <- result do
|
with {:ok, %{"status" => "enqueued"}} <- result do
|
||||||
# Added successfully
|
# Added successfully
|
||||||
:ok
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
# There was an error, report it
|
|
||||||
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
|
||||||
{:error, result}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# The post isn't something we can search, that's ok
|
|
||||||
:ok
|
:ok
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
# There was an error, report it
|
||||||
|
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
||||||
|
{:error, result}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Search.QdrantSearch do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config.Getting, as: Config
|
alias Pleroma.Config.Getting, as: Config
|
||||||
|
alias Pleroma.Object
|
||||||
|
|
||||||
alias __MODULE__.OpenAIClient
|
alias __MODULE__.OpenAIClient
|
||||||
alias __MODULE__.QdrantClient
|
alias __MODULE__.QdrantClient
|
||||||
|
|
@ -82,23 +83,18 @@ defmodule Pleroma.Search.QdrantSearch do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def add_to_index(activity) do
|
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||||
# This will only index public or unlisted notes
|
search_data = object_to_search_data(object)
|
||||||
maybe_search_data = object_to_search_data(activity.object)
|
|
||||||
|
|
||||||
if activity.data["type"] == "Create" and maybe_search_data do
|
with {:ok, embedding} <- get_embedding(search_data.content),
|
||||||
with {:ok, embedding} <- get_embedding(maybe_search_data.content),
|
{:ok, %{status: 200}} <-
|
||||||
{:ok, %{status: 200}} <-
|
QdrantClient.put(
|
||||||
QdrantClient.put(
|
"/collections/posts/points",
|
||||||
"/collections/posts/points",
|
build_index_payload(activity, embedding)
|
||||||
build_index_payload(activity, embedding)
|
) do
|
||||||
) do
|
|
||||||
:ok
|
|
||||||
else
|
|
||||||
e -> {:error, e}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
:ok
|
:ok
|
||||||
|
else
|
||||||
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
|
||||||
def store(upload, opts \\ []) do
|
def store(upload, opts \\ []) do
|
||||||
opts = get_opts(opts)
|
opts = get_opts(opts)
|
||||||
|
|
||||||
with {:ok, upload} <- prepare_upload(upload, opts),
|
with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts),
|
||||||
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
||||||
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
||||||
description = get_description(upload),
|
description = get_description(upload),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Utils do
|
||||||
dir
|
dir
|
||||||
|> File.ls!()
|
|> File.ls!()
|
||||||
|> Enum.map(&Path.join(dir, &1))
|
|> Enum.map(&Path.join(dir, &1))
|
||||||
|> Kernel.ParallelCompiler.compile()
|
|> Kernel.ParallelCompiler.compile(return_diagnostics: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
||||||
|
|
@ -1618,6 +1618,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
||||||
defp normalize_image(_), do: nil
|
defp normalize_image(_), do: nil
|
||||||
|
|
||||||
|
defp normalize_also_known_as(urls) when is_list(urls), do: urls
|
||||||
|
defp normalize_also_known_as(url) when is_binary(url), do: [url]
|
||||||
|
defp normalize_also_known_as(nil), do: []
|
||||||
|
|
||||||
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
||||||
Map.put(map, "name", description)
|
Map.put(map, "name", description)
|
||||||
end
|
end
|
||||||
|
|
@ -1693,7 +1697,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
featured_address: featured_address,
|
featured_address: featured_address,
|
||||||
bio: data["summary"] || "",
|
bio: data["summary"] || "",
|
||||||
actor_type: actor_type,
|
actor_type: actor_type,
|
||||||
also_known_as: Map.get(data, "alsoKnownAs", []),
|
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
|
||||||
public_key: public_key,
|
public_key: public_key,
|
||||||
inbox: data["inbox"],
|
inbox: data["inbox"],
|
||||||
shared_inbox: shared_inbox,
|
shared_inbox: shared_inbox,
|
||||||
|
|
|
||||||
|
|
@ -332,21 +332,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
|
|
||||||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||||
def announce(actor, object, options \\ []) do
|
def announce(actor, object, options \\ []) do
|
||||||
public? = Keyword.get(options, :public, false)
|
visibility = Keyword.get(options, :visibility, "public")
|
||||||
|
|
||||||
to =
|
{to, cc} =
|
||||||
cond do
|
if actor.ap_id == Relay.ap_id() do
|
||||||
actor.ap_id == Relay.ap_id() ->
|
{[actor.follower_address], []}
|
||||||
[actor.follower_address]
|
else
|
||||||
|
Pleroma.Web.CommonAPI.Utils.get_to_and_cc_for_visibility(
|
||||||
public? and Visibility.local_public?(object) ->
|
visibility,
|
||||||
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
actor.follower_address,
|
||||||
|
nil,
|
||||||
public? ->
|
[object.data["actor"]]
|
||||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
)
|
||||||
|
|
||||||
true ->
|
|
||||||
[actor.follower_address, object.data["actor"]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
@ -355,6 +352,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
"object" => object.data["id"],
|
"object" => object.data["id"],
|
||||||
"to" => to,
|
"to" => to,
|
||||||
|
"cc" => cc,
|
||||||
"context" => object.data["context"],
|
"context" => object.data["context"],
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
"published" => Utils.make_date()
|
"published" => Utils.make_date()
|
||||||
|
|
|
||||||
|
|
@ -35,32 +35,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
def render("endpoints.json", _), do: %{}
|
def render("endpoints.json", _), do: %{}
|
||||||
|
|
||||||
def render("service.json", %{user: user}) do
|
def render("service.json", %{user: user}) do
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
Map.merge(common_actor_fields(user), %{
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
|
||||||
public_key = :public_key.pem_encode([public_key])
|
|
||||||
|
|
||||||
endpoints = render("endpoints.json", %{user: user})
|
|
||||||
|
|
||||||
%{
|
|
||||||
"id" => user.ap_id,
|
|
||||||
"type" => "Application",
|
"type" => "Application",
|
||||||
"following" => "#{user.ap_id}/following",
|
|
||||||
"followers" => "#{user.ap_id}/followers",
|
|
||||||
"inbox" => "#{user.ap_id}/inbox",
|
|
||||||
"outbox" => "#{user.ap_id}/outbox",
|
|
||||||
"name" => "Pleroma",
|
"name" => "Pleroma",
|
||||||
"summary" =>
|
"summary" =>
|
||||||
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
||||||
"url" => user.ap_id,
|
|
||||||
"manuallyApprovesFollowers" => false,
|
"manuallyApprovesFollowers" => false,
|
||||||
"publicKey" => %{
|
|
||||||
"id" => "#{user.ap_id}#main-key",
|
|
||||||
"owner" => user.ap_id,
|
|
||||||
"publicKeyPem" => public_key
|
|
||||||
},
|
|
||||||
"endpoints" => endpoints,
|
|
||||||
"invisible" => User.invisible?(user)
|
"invisible" => User.invisible?(user)
|
||||||
}
|
})
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -77,13 +59,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("user.json", %{user: user}) do
|
def render("user.json", %{user: user}) do
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
|
||||||
public_key = :public_key.pem_encode([public_key])
|
|
||||||
user = User.sanitize_html(user)
|
user = User.sanitize_html(user)
|
||||||
|
|
||||||
endpoints = render("endpoints.json", %{user: user})
|
|
||||||
|
|
||||||
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
||||||
|
|
||||||
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||||
|
|
@ -102,25 +79,9 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
do: Date.to_iso8601(user.birthday),
|
do: Date.to_iso8601(user.birthday),
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
%{
|
Map.merge(common_actor_fields(user), %{
|
||||||
"id" => user.ap_id,
|
|
||||||
"type" => user.actor_type,
|
|
||||||
"following" => "#{user.ap_id}/following",
|
|
||||||
"followers" => "#{user.ap_id}/followers",
|
|
||||||
"inbox" => "#{user.ap_id}/inbox",
|
|
||||||
"outbox" => "#{user.ap_id}/outbox",
|
|
||||||
"featured" => "#{user.ap_id}/collections/featured",
|
"featured" => "#{user.ap_id}/collections/featured",
|
||||||
"preferredUsername" => user.nickname,
|
"preferredUsername" => user.nickname,
|
||||||
"name" => user.name,
|
|
||||||
"summary" => user.bio,
|
|
||||||
"url" => user.ap_id,
|
|
||||||
"manuallyApprovesFollowers" => user.is_locked,
|
|
||||||
"publicKey" => %{
|
|
||||||
"id" => "#{user.ap_id}#main-key",
|
|
||||||
"owner" => user.ap_id,
|
|
||||||
"publicKeyPem" => public_key
|
|
||||||
},
|
|
||||||
"endpoints" => endpoints,
|
|
||||||
"attachment" => fields,
|
"attachment" => fields,
|
||||||
"tag" => emoji_tags,
|
"tag" => emoji_tags,
|
||||||
# Note: key name is indeed "discoverable" (not an error)
|
# Note: key name is indeed "discoverable" (not an error)
|
||||||
|
|
@ -130,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
"vcard:bday" => birthday,
|
"vcard:bday" => birthday,
|
||||||
"webfinger" => "acct:#{User.full_nickname(user)}",
|
"webfinger" => "acct:#{User.full_nickname(user)}",
|
||||||
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
|
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
|
||||||
}
|
})
|
||||||
|> Map.merge(
|
|> Map.merge(
|
||||||
maybe_make_image(
|
maybe_make_image(
|
||||||
&User.avatar_url/2,
|
&User.avatar_url/2,
|
||||||
|
|
@ -309,6 +270,33 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp common_actor_fields(%User{} = user) do
|
||||||
|
endpoints = render("endpoints.json", %{user: user})
|
||||||
|
|
||||||
|
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||||
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
|
||||||
|
%{
|
||||||
|
"id" => user.ap_id,
|
||||||
|
"type" => user.actor_type,
|
||||||
|
"following" => "#{user.ap_id}/following",
|
||||||
|
"followers" => "#{user.ap_id}/followers",
|
||||||
|
"inbox" => "#{user.ap_id}/inbox",
|
||||||
|
"outbox" => "#{user.ap_id}/outbox",
|
||||||
|
"name" => user.name,
|
||||||
|
"summary" => user.bio,
|
||||||
|
"url" => user.ap_id,
|
||||||
|
"manuallyApprovesFollowers" => user.is_locked,
|
||||||
|
"endpoints" => endpoints,
|
||||||
|
"publicKey" => %{
|
||||||
|
"id" => "#{user.ap_id}#main-key",
|
||||||
|
"owner" => user.ap_id,
|
||||||
|
"publicKeyPem" => public_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_put_total_items(map, false, _total), do: map
|
defp maybe_put_total_items(map, false, _total), do: map
|
||||||
|
|
||||||
defp maybe_put_total_items(map, true, total) do
|
defp maybe_put_total_items(map, true, total) do
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,14 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
|
||||||
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
|
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
|
defp cast_and_validate(
|
||||||
|
spec,
|
||||||
|
operation,
|
||||||
|
%Conn{} = conn,
|
||||||
|
content_type,
|
||||||
|
false = _strict,
|
||||||
|
cast_opts
|
||||||
|
) do
|
||||||
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
|
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
|
||||||
{:ok, conn} ->
|
{:ok, conn} ->
|
||||||
{:ok, conn}
|
{:ok, conn}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
||||||
summary: "Create a list",
|
summary: "Create a list",
|
||||||
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
|
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
|
||||||
operationId: "ListController.create",
|
operationId: "ListController.create",
|
||||||
requestBody: create_update_request(),
|
requestBody: create_request(),
|
||||||
security: [%{"oAuth" => ["write:lists"]}],
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("List", "application/json", List),
|
200 => Operation.response("List", "application/json", List),
|
||||||
|
|
@ -68,7 +68,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
||||||
description: "Change the title of a list",
|
description: "Change the title of a list",
|
||||||
operationId: "ListController.update",
|
operationId: "ListController.update",
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
requestBody: create_update_request(),
|
requestBody: update_request(),
|
||||||
security: [%{"oAuth" => ["write:lists"]}],
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("List", "application/json", List),
|
200 => Operation.response("List", "application/json", List),
|
||||||
|
|
@ -164,14 +164,15 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_update_request do
|
defp create_request do
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
%Schema{
|
%Schema{
|
||||||
description: "POST body for creating or updating a List",
|
description: "POST body for creating a List",
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
title: %Schema{type: :string, description: "List title"}
|
title: %Schema{type: :string, description: "List title"},
|
||||||
|
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||||
},
|
},
|
||||||
required: [:title]
|
required: [:title]
|
||||||
},
|
},
|
||||||
|
|
@ -179,6 +180,21 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "PUT body for updating a List",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
title: %Schema{type: :string, description: "List title"},
|
||||||
|
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp add_remove_accounts_request(required) when is_boolean(required) do
|
defp add_remove_accounts_request(required) when is_boolean(required) do
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
defmodule Pleroma.Web.ApiSpec.PleromaUtilOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Custom emojis"],
|
tags: ["Custom emojis"],
|
||||||
summary: "List all custom emojis",
|
summary: "List all custom emojis",
|
||||||
operationId: "UtilController.emoji",
|
operationId: "PleromaAPI.UtilController.emoji",
|
||||||
parameters: [],
|
parameters: [],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -48,7 +48,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Others"],
|
tags: ["Others"],
|
||||||
summary: "Dump frontend configurations",
|
summary: "Dump frontend configurations",
|
||||||
operationId: "UtilController.frontend_configurations",
|
operationId: "PleromaAPI.UtilController.frontend_configurations",
|
||||||
parameters: [],
|
parameters: [],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Change account password",
|
summary: "Change account password",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.change_password",
|
operationId: "PleromaAPI.UtilController.change_password",
|
||||||
requestBody: request_body("Parameters", change_password_request(), required: true),
|
requestBody: request_body("Parameters", change_password_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Change account email",
|
summary: "Change account email",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.change_email",
|
operationId: "PleromaAPI.UtilController.change_email",
|
||||||
requestBody: request_body("Parameters", change_email_request(), required: true),
|
requestBody: request_body("Parameters", change_email_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -141,7 +141,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Settings"],
|
tags: ["Settings"],
|
||||||
summary: "Update Notification Settings",
|
summary: "Update Notification Settings",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.update_notification_settings",
|
operationId: "PleromaAPI.UtilController.update_notification_settings",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(
|
Operation.parameter(
|
||||||
:block_from_strangers,
|
:block_from_strangers,
|
||||||
|
|
@ -173,7 +173,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Disable Account",
|
summary: "Disable Account",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.disable_account",
|
operationId: "PleromaAPI.UtilController.disable_account",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:password, :query, :string, "Password")
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
],
|
],
|
||||||
|
|
@ -193,7 +193,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Delete Account",
|
summary: "Delete Account",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.delete_account",
|
operationId: "PleromaAPI.UtilController.delete_account",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:password, :query, :string, "Password")
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
],
|
],
|
||||||
|
|
@ -212,7 +212,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
def captcha_operation do
|
def captcha_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
summary: "Get a captcha",
|
summary: "Get a captcha",
|
||||||
operationId: "UtilController.captcha",
|
operationId: "PleromaAPI.UtilController.captcha",
|
||||||
tags: ["Others"],
|
tags: ["Others"],
|
||||||
parameters: [],
|
parameters: [],
|
||||||
responses: %{
|
responses: %{
|
||||||
|
|
@ -226,7 +226,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Move account",
|
summary: "Move account",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.move_account",
|
operationId: "PleromaAPI.UtilController.move_account",
|
||||||
requestBody: request_body("Parameters", move_account_request(), required: true),
|
requestBody: request_body("Parameters", move_account_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -262,7 +262,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "List account aliases",
|
summary: "List account aliases",
|
||||||
security: [%{"oAuth" => ["read:accounts"]}],
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
operationId: "UtilController.list_aliases",
|
operationId: "PleromaAPI.UtilController.list_aliases",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Success", "application/json", %Schema{
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
|
@ -286,7 +286,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Add an alias to this account",
|
summary: "Add an alias to this account",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.add_alias",
|
operationId: "PleromaAPI.UtilController.add_alias",
|
||||||
requestBody: request_body("Parameters", add_alias_request(), required: true),
|
requestBody: request_body("Parameters", add_alias_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -326,7 +326,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Account credentials"],
|
tags: ["Account credentials"],
|
||||||
summary: "Delete an alias from this account",
|
summary: "Delete an alias from this account",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.delete_alias",
|
operationId: "PleromaAPI.UtilController.delete_alias",
|
||||||
requestBody: request_body("Parameters", delete_alias_request(), required: true),
|
requestBody: request_body("Parameters", delete_alias_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
@ -366,7 +366,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
tags: ["Others"],
|
tags: ["Others"],
|
||||||
summary: "Quick status check on the instance",
|
summary: "Quick status check on the instance",
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
operationId: "UtilController.healthcheck",
|
operationId: "PleromaAPI.UtilController.healthcheck",
|
||||||
parameters: [],
|
parameters: [],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
|
200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
|
||||||
|
|
@ -376,52 +376,6 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def remote_subscribe_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Remote interaction"],
|
|
||||||
summary: "Remote Subscribe",
|
|
||||||
operationId: "UtilController.remote_subscribe",
|
|
||||||
parameters: [],
|
|
||||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_interaction_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Remote interaction"],
|
|
||||||
summary: "Remote interaction",
|
|
||||||
operationId: "UtilController.remote_interaction",
|
|
||||||
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
|
||||||
responses: %{
|
|
||||||
200 =>
|
|
||||||
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp remote_interaction_request do
|
|
||||||
%Schema{
|
|
||||||
title: "RemoteInteractionRequest",
|
|
||||||
description: "POST body for remote interaction",
|
|
||||||
type: :object,
|
|
||||||
required: [:ap_id, :profile],
|
|
||||||
properties: %{
|
|
||||||
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
|
||||||
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_subscribe_form_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Remote interaction"],
|
|
||||||
summary: "Show remote subscribe form",
|
|
||||||
operationId: "UtilController.show_subscribe_form",
|
|
||||||
parameters: [],
|
|
||||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp delete_account_request do
|
defp delete_account_request do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "AccountDeleteRequest",
|
title: "AccountDeleteRequest",
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.RemoteInteractionOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_interaction_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Remote interaction",
|
||||||
|
operationId: "RemoteInteractionController.remote_interaction",
|
||||||
|
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remote_interaction_request do
|
||||||
|
%Schema{
|
||||||
|
title: "RemoteInteractionRequest",
|
||||||
|
description: "POST body for remote interaction",
|
||||||
|
type: :object,
|
||||||
|
required: [:ap_id, :profile],
|
||||||
|
properties: %{
|
||||||
|
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
||||||
|
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Display follow form",
|
||||||
|
operationId: "RemoteInteractionController.follow",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Web Page", "text/html", %Schema{type: :string}),
|
||||||
|
302 => Operation.response("Redirect to the status page", nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_follow_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Perform follow activity",
|
||||||
|
operationId: "RemoteInteractionController.do_follow",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Web page", "text/html", %Schema{type: :string}),
|
||||||
|
302 => Operation.response("Redirect to the account page", nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_interaction_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Authorize remote interaction",
|
||||||
|
operationId: "RemoteInteractionController.authorize_interaction",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
302 => Operation.response("Redirect to remote_interaction path", nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_subscribe_form_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Show remote subscribe form",
|
||||||
|
operationId: "RemoteInteractionController.show_subscribe_form",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Remote interaction"],
|
||||||
|
summary: "Remote Subscribe",
|
||||||
|
operationId: "RemoteInteractionController.remote_subscribe",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -17,11 +17,11 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
||||||
def call(conn, errors) do
|
def call(conn, errors) do
|
||||||
errors =
|
errors =
|
||||||
Enum.map(errors, fn
|
Enum.map(errors, fn
|
||||||
%{name: nil, reason: :invalid_enum} = err ->
|
%OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err ->
|
||||||
%OpenApiSpex.Cast.Error{err | name: err.value}
|
%{err | name: err.value}
|
||||||
|
|
||||||
%{name: nil} = err ->
|
%OpenApiSpex.Cast.Error{name: nil} = err ->
|
||||||
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
%{err | name: List.last(err.path)}
|
||||||
|
|
||||||
err ->
|
err ->
|
||||||
err
|
err
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,18 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BookmarkFolder do
|
||||||
properties: %{
|
properties: %{
|
||||||
id: FlakeID,
|
id: FlakeID,
|
||||||
name: %Schema{type: :string, description: "Folder name"},
|
name: %Schema{type: :string, description: "Folder name"},
|
||||||
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true}
|
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true},
|
||||||
|
emoji_url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "URL of the folder emoji if it's a custom emoji, null otherwise",
|
||||||
|
nullable: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
"id" => "9toJCu5YZW7O7gfvH6",
|
"id" => "9toJCu5YZW7O7gfvH6",
|
||||||
"name" => "Read later",
|
"name" => "Read later",
|
||||||
"emoji" => nil
|
"emoji" => nil,
|
||||||
|
"emoji_url" => nil
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.List do
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
id: %Schema{type: :string, description: "The internal database ID of the list"},
|
id: %Schema{type: :string, description: "The internal database ID of the list"},
|
||||||
title: %Schema{type: :string, description: "The user-defined title of the list"}
|
title: %Schema{type: :string, description: "The user-defined title of the list"},
|
||||||
|
exclusive: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Whether members of the list should be removed from the “Home” feed"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
"id" => "12249",
|
"id" => "12249",
|
||||||
|
|
|
||||||
|
|
@ -222,8 +222,8 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||||
object = %Object{} <- Object.normalize(activity, fetch: false),
|
object = %Object{} <- Object.normalize(activity, fetch: false),
|
||||||
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
||||||
public = public_announce?(object, params),
|
visibility = announce_visibility(object, params),
|
||||||
{:ok, announce, _} <- Builder.announce(user, object, public: public),
|
{:ok, announce, _} <- Builder.announce(user, object, visibility: visibility),
|
||||||
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
|
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
|
|
@ -407,13 +407,11 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp public_announce?(_, %{visibility: visibility})
|
def announce_visibility(_, %{visibility: visibility})
|
||||||
when visibility in ~w{public unlisted private direct},
|
when visibility in ~w{public unlisted private direct local},
|
||||||
do: visibility in ~w(public unlisted)
|
do: visibility
|
||||||
|
|
||||||
defp public_announce?(object, _) do
|
def announce_visibility(object, _), do: Visibility.get_visibility(object)
|
||||||
Visibility.public?(object)
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_visibility(map(), map() | nil, Participation.t() | nil) ::
|
@spec get_visibility(map(), map() | nil, Participation.t() | nil) ::
|
||||||
{String.t() | nil, String.t() | nil}
|
{String.t() | nil, String.t() | nil}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|> validate()
|
|> validate()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp listen_object(draft) do
|
defp listen_object(%__MODULE__{} = draft) do
|
||||||
object =
|
object =
|
||||||
draft.params
|
draft.params
|
||||||
|> Map.take([:album, :artist, :title, :length])
|
|> Map.take([:album, :artist, :title, :length])
|
||||||
|
|
@ -99,34 +99,34 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|> Map.put("cc", draft.cc)
|
|> Map.put("cc", draft.cc)
|
||||||
|> Map.put("actor", draft.user.ap_id)
|
|> Map.put("actor", draft.user.ap_id)
|
||||||
|
|
||||||
%__MODULE__{draft | object: object}
|
%{draft | object: object}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_params(draft, params) do
|
defp put_params(%__MODULE__{} = draft, params) do
|
||||||
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||||
%__MODULE__{draft | params: params}
|
%{draft | params: params}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp status(%{params: %{status: status}} = draft) do
|
defp status(%__MODULE__{params: %{status: status}} = draft) do
|
||||||
%__MODULE__{draft | status: String.trim(status)}
|
%{draft | status: String.trim(status)}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp summary(%{params: params} = draft) do
|
defp summary(%__MODULE__{params: params} = draft) do
|
||||||
%__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}
|
%{draft | summary: Map.get(params, :spoiler_text, "")}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp full_payload(%{status: status, summary: summary} = draft) do
|
defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do
|
||||||
full_payload = String.trim(status <> summary)
|
full_payload = String.trim(status <> summary)
|
||||||
|
|
||||||
case Utils.validate_character_limit(full_payload, draft.attachments) do
|
case Utils.validate_character_limit(full_payload, draft.attachments) do
|
||||||
:ok -> %__MODULE__{draft | full_payload: full_payload}
|
:ok -> %{draft | full_payload: full_payload}
|
||||||
{:error, message} -> add_error(draft, message)
|
{:error, message} -> add_error(draft, message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp attachments(%{params: params} = draft) do
|
defp attachments(%__MODULE__{params: params} = draft) do
|
||||||
attachments = Utils.attachments_from_ids(params, draft.user)
|
attachments = Utils.attachments_from_ids(params, draft.user)
|
||||||
draft = %__MODULE__{draft | attachments: attachments}
|
draft = %{draft | attachments: attachments}
|
||||||
|
|
||||||
case Utils.validate_attachments_count(attachments) do
|
case Utils.validate_attachments_count(attachments) do
|
||||||
:ok -> draft
|
:ok -> draft
|
||||||
|
|
@ -134,9 +134,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
|
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft
|
||||||
|
|
||||||
defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do
|
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft)
|
||||||
|
when is_binary(id) do
|
||||||
# If a post was deleted all its activities (except the newly added Delete) are purged too,
|
# If a post was deleted all its activities (except the newly added Delete) are purged too,
|
||||||
# thus lookup by Create db ID will yield nil just as if it never existed in the first place.
|
# thus lookup by Create db ID will yield nil just as if it never existed in the first place.
|
||||||
#
|
#
|
||||||
|
|
@ -148,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||||
true <- Visibility.visible_for_user?(activity, draft.user),
|
true <- Visibility.visible_for_user?(activity, draft.user),
|
||||||
{_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do
|
{_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do
|
||||||
%__MODULE__{draft | in_reply_to: activity}
|
%{draft | in_reply_to: activity}
|
||||||
else
|
else
|
||||||
nil ->
|
nil ->
|
||||||
add_error(draft, dgettext("errors", "Cannot reply to a deleted status"))
|
add_error(draft, dgettext("errors", "Cannot reply to a deleted status"))
|
||||||
|
|
@ -166,40 +167,43 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do
|
defp in_reply_to(
|
||||||
%__MODULE__{draft | in_reply_to: in_reply_to}
|
%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft
|
||||||
|
) do
|
||||||
|
%{draft | in_reply_to: in_reply_to}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp in_reply_to(draft), do: draft
|
defp in_reply_to(draft), do: draft
|
||||||
|
|
||||||
defp quote_post(%{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do
|
defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft)
|
||||||
|
when not_empty_string(id) do
|
||||||
case Activity.get_by_id_with_object(id) do
|
case Activity.get_by_id_with_object(id) do
|
||||||
%Activity{} = activity ->
|
%Activity{} = activity ->
|
||||||
%__MODULE__{draft | quote_post: activity}
|
%{draft | quote_post: activity}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
draft
|
draft
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do
|
defp quote_post(%__MODULE__{params: %{quote_id: id}} = draft) when not_empty_string(id) do
|
||||||
quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)})
|
quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp quote_post(draft), do: draft
|
defp quote_post(draft), do: draft
|
||||||
|
|
||||||
defp in_reply_to_conversation(draft) do
|
defp in_reply_to_conversation(%__MODULE__{} = draft) do
|
||||||
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
|
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
|
||||||
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
|
%{draft | in_reply_to_conversation: in_reply_to_conversation}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp visibility(%{params: params} = draft) do
|
defp visibility(%__MODULE__{params: params} = draft) do
|
||||||
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
|
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
|
||||||
{visibility, "direct"} when visibility != "direct" ->
|
{visibility, "direct"} when visibility != "direct" ->
|
||||||
add_error(draft, dgettext("errors", "The message visibility must be direct"))
|
add_error(draft, dgettext("errors", "The message visibility must be direct"))
|
||||||
|
|
||||||
{visibility, _} ->
|
{visibility, _} ->
|
||||||
%__MODULE__{draft | visibility: visibility}
|
%{draft | visibility: visibility}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -215,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
defp quoting_visibility(%{quote_post: %Activity{}} = draft) do
|
defp quoting_visibility(%__MODULE__{quote_post: %Activity{}} = draft) do
|
||||||
with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false),
|
with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false),
|
||||||
true <- can_quote?(draft, object, Visibility.get_visibility(object)) do
|
true <- can_quote?(draft, object, Visibility.get_visibility(object)) do
|
||||||
draft
|
draft
|
||||||
|
|
@ -226,24 +230,24 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|
|
||||||
defp quoting_visibility(draft), do: draft
|
defp quoting_visibility(draft), do: draft
|
||||||
|
|
||||||
defp expires_at(draft) do
|
defp expires_at(%__MODULE__{} = draft) do
|
||||||
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
|
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
|
||||||
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
|
{:ok, expires_at} -> %{draft | expires_at: expires_at}
|
||||||
{:error, message} -> add_error(draft, message)
|
{:error, message} -> add_error(draft, message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp poll(draft) do
|
defp poll(%__MODULE__{} = draft) do
|
||||||
case Utils.make_poll_data(draft.params) do
|
case Utils.make_poll_data(draft.params) do
|
||||||
{:ok, {poll, poll_emoji}} ->
|
{:ok, {poll, poll_emoji}} ->
|
||||||
%__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
|
%{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
|
||||||
|
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
add_error(draft, message)
|
add_error(draft, message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp content(%{mentions: mentions} = draft) do
|
defp content(%__MODULE__{mentions: mentions} = draft) do
|
||||||
{content_html, mentioned_users, tags} = Utils.make_content_html(draft)
|
{content_html, mentioned_users, tags} = Utils.make_content_html(draft)
|
||||||
|
|
||||||
mentioned_ap_ids =
|
mentioned_ap_ids =
|
||||||
|
|
@ -254,25 +258,25 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|> Kernel.++(mentioned_ap_ids)
|
|> Kernel.++(mentioned_ap_ids)
|
||||||
|> Utils.get_addressed_users(draft.params[:to])
|
|> Utils.get_addressed_users(draft.params[:to])
|
||||||
|
|
||||||
%__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags}
|
%{draft | content_html: content_html, mentions: mentions, tags: tags}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp to_and_cc(draft) do
|
defp to_and_cc(%__MODULE__{} = draft) do
|
||||||
{to, cc} = Utils.get_to_and_cc(draft)
|
{to, cc} = Utils.get_to_and_cc(draft)
|
||||||
%__MODULE__{draft | to: to, cc: cc}
|
%{draft | to: to, cc: cc}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp context(draft) do
|
defp context(%__MODULE__{} = draft) do
|
||||||
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
|
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
|
||||||
%__MODULE__{draft | context: context}
|
%{draft | context: context}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp sensitive(draft) do
|
defp sensitive(%__MODULE__{} = draft) do
|
||||||
sensitive = draft.params[:sensitive]
|
sensitive = draft.params[:sensitive]
|
||||||
%__MODULE__{draft | sensitive: sensitive}
|
%{draft | sensitive: sensitive}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp language(draft) do
|
defp language(%__MODULE__{} = draft) do
|
||||||
language =
|
language =
|
||||||
with language <- draft.params[:language],
|
with language <- draft.params[:language],
|
||||||
true <- good_locale_code?(language) do
|
true <- good_locale_code?(language) do
|
||||||
|
|
@ -281,10 +285,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
_ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
|
_ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
|
||||||
end
|
end
|
||||||
|
|
||||||
%__MODULE__{draft | language: language}
|
%{draft | language: language}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp object(draft) do
|
defp object(%__MODULE__{} = draft) do
|
||||||
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
||||||
|
|
||||||
# Sometimes people create posts with subject containing emoji,
|
# Sometimes people create posts with subject containing emoji,
|
||||||
|
|
@ -325,15 +329,15 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|> Map.put("generator", draft.params[:generator])
|
|> Map.put("generator", draft.params[:generator])
|
||||||
|> Map.put("language", draft.language)
|
|> Map.put("language", draft.language)
|
||||||
|
|
||||||
%__MODULE__{draft | object: object}
|
%{draft | object: object}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp preview?(draft) do
|
defp preview?(%__MODULE__{} = draft) do
|
||||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||||
%__MODULE__{draft | preview?: preview?}
|
%{draft | preview?: preview?}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp changes(draft) do
|
defp changes(%__MODULE__{} = draft) do
|
||||||
direct? = draft.visibility == "direct"
|
direct? = draft.visibility == "direct"
|
||||||
additional = %{"cc" => draft.cc, "directMessage" => direct?}
|
additional = %{"cc" => draft.cc, "directMessage" => direct?}
|
||||||
|
|
||||||
|
|
@ -353,14 +357,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
}
|
}
|
||||||
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
|
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
|
||||||
|
|
||||||
%__MODULE__{draft | changes: changes}
|
%{draft | changes: changes}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
|
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
|
||||||
defp with_valid(draft, _func), do: draft
|
defp with_valid(draft, _func), do: draft
|
||||||
|
|
||||||
defp add_error(draft, message) do
|
defp add_error(%__MODULE__{} = draft, message) do
|
||||||
%__MODULE__{draft | valid?: false, errors: [message | draft.errors]}
|
%{draft | valid?: false, errors: [message | draft.errors]}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate(%{valid?: true} = draft), do: {:ok, draft}
|
defp validate(%{valid?: true} = draft), do: {:ok, draft}
|
||||||
|
|
|
||||||
|
|
@ -75,48 +75,70 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
{Enum.map(participation.recipients, & &1.ap_id), []}
|
{Enum.map(participation.recipients, & &1.ap_id), []}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(%{visibility: visibility} = draft) when visibility in ["public", "local"] do
|
def get_to_and_cc(%{visibility: visibility} = draft) do
|
||||||
to =
|
# If the OP is a DM already, add the implicit actor
|
||||||
case visibility do
|
mentions =
|
||||||
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
|
if visibility == "direct" && draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
|
||||||
"local" -> [Utils.as_local_public() | draft.mentions]
|
Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions])
|
||||||
|
else
|
||||||
|
draft.mentions
|
||||||
end
|
end
|
||||||
|
|
||||||
cc = [draft.user.follower_address]
|
get_to_and_cc_for_visibility(
|
||||||
|
visibility,
|
||||||
if draft.in_reply_to do
|
draft.user.follower_address,
|
||||||
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
|
draft.in_reply_to && draft.in_reply_to.data["actor"],
|
||||||
else
|
mentions
|
||||||
{to, cc}
|
)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(%{visibility: "unlisted"} = draft) do
|
def get_to_and_cc_for_visibility("public", follower_collection, parent_actor, mentions) do
|
||||||
to = [draft.user.follower_address | draft.mentions]
|
scope_addr = Pleroma.Constants.as_public()
|
||||||
cc = [Pleroma.Constants.as_public()]
|
|
||||||
|
|
||||||
if draft.in_reply_to do
|
to =
|
||||||
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
|
if parent_actor,
|
||||||
else
|
do: Enum.uniq([parent_actor, scope_addr | mentions]),
|
||||||
{to, cc}
|
else: [scope_addr | mentions]
|
||||||
end
|
|
||||||
|
{to, [follower_collection]}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(%{visibility: "private"} = draft) do
|
def get_to_and_cc_for_visibility("local", follower_collection, parent_actor, mentions) do
|
||||||
{to, cc} = get_to_and_cc(struct(draft, visibility: "direct"))
|
recipients =
|
||||||
{[draft.user.follower_address | to], cc}
|
if parent_actor,
|
||||||
|
do: Enum.uniq([parent_actor | mentions]),
|
||||||
|
else: mentions
|
||||||
|
|
||||||
|
to = [
|
||||||
|
Utils.as_local_public()
|
||||||
|
| Enum.filter(recipients, fn addr ->
|
||||||
|
String.starts_with?(addr, Pleroma.Web.Endpoint.url() <> "/")
|
||||||
|
end)
|
||||||
|
]
|
||||||
|
|
||||||
|
{to, [follower_collection]}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(%{visibility: "direct"} = draft) do
|
def get_to_and_cc_for_visibility("unlisted", follower_collection, parent_actor, mentions) do
|
||||||
# If the OP is a DM already, add the implicit actor.
|
to =
|
||||||
if draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
|
if parent_actor,
|
||||||
{Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions]), []}
|
do: Enum.uniq([parent_actor, follower_collection | mentions]),
|
||||||
else
|
else: [follower_collection | mentions]
|
||||||
{draft.mentions, []}
|
|
||||||
end
|
{to, [Pleroma.Constants.as_public()]}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(%{visibility: {:list, _}, mentions: mentions}), do: {mentions, []}
|
def get_to_and_cc_for_visibility("private", follower_collection, _, mentions) do
|
||||||
|
{[follower_collection | mentions], []}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_to_and_cc_for_visibility("direct", _, _, mentions) do
|
||||||
|
{mentions, []}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_to_and_cc_for_visibility({:list, _}, _, _, mentions) do
|
||||||
|
{mentions, []}
|
||||||
|
end
|
||||||
|
|
||||||
def get_addressed_users(_, to) when is_list(to) do
|
def get_addressed_users(_, to) when is_list(to) do
|
||||||
User.get_ap_ids_by_nicknames(to)
|
User.get_ap_ids_by_nicknames(to)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ defmodule Pleroma.Web.EmbedController do
|
||||||
conn
|
conn
|
||||||
|> delete_resp_header("x-frame-options")
|
|> delete_resp_header("x-frame-options")
|
||||||
|> delete_resp_header("content-security-policy")
|
|> delete_resp_header("content-security-policy")
|
||||||
|
|> put_layout({Pleroma.Web.LayoutView, :embed})
|
||||||
|> render("show.html",
|
|> render("show.html",
|
||||||
activity: activity,
|
activity: activity,
|
||||||
author: User.sanitize_html(author),
|
author: User.sanitize_html(author),
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,18 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def live_dashboard(conn, _params) do
|
def live_dashboard(conn, %{"path" => path}) do
|
||||||
|
query_params = conn.query_string
|
||||||
|
|
||||||
|
redirect_path =
|
||||||
|
if query_params == "" do
|
||||||
|
"/pleroma/live_dashboard/#{path}"
|
||||||
|
else
|
||||||
|
"/pleroma/live_dashboard/#{path}?#{query_params}"
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> redirect(to: "/pleroma/live_dashboard")
|
|> redirect(to: redirect_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirector(conn, _params, code \\ 200) do
|
def redirector(conn, _params, code \\ 200) do
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
alias Pleroma.Web.OAuth.OAuthController
|
alias Pleroma.Web.OAuth.OAuthController
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.Plugs.RateLimiter
|
alias Pleroma.Web.Plugs.RateLimiter
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.Registration
|
||||||
alias Pleroma.Web.Utils.Params
|
alias Pleroma.Web.Utils.Params
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
|
|
@ -111,8 +111,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
_params
|
_params
|
||||||
) do
|
) do
|
||||||
with :ok <- validate_email_param(params),
|
with :ok <- validate_email_param(params),
|
||||||
:ok <- TwitterAPI.validate_captcha(app, params),
|
:ok <- Registration.validate_captcha(app, params),
|
||||||
{:ok, user} <- TwitterAPI.register_user(params),
|
{:ok, user} <- Registration.register_user(params),
|
||||||
{_, {:ok, token}} <-
|
{_, {:ok, token}} <-
|
||||||
{:login, OAuthController.login(user, app, app.scopes)} do
|
{:login, OAuthController.login(user, app, app.scopes)} do
|
||||||
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.Registration
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
||||||
def password_reset(conn, params) do
|
def password_reset(conn, params) do
|
||||||
nickname_or_email = params["email"] || params["nickname"]
|
nickname_or_email = params["email"] || params["nickname"]
|
||||||
|
|
||||||
TwitterAPI.password_reset(nickname_or_email)
|
Registration.password_reset(nickname_or_email)
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -28,27 +28,27 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
||||||
|
|
||||||
# POST /api/v1/lists
|
# POST /api/v1/lists
|
||||||
def create(
|
def create(
|
||||||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: %{title: title}}}} =
|
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: params}}} =
|
||||||
conn,
|
conn,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(params, user) do
|
||||||
render(conn, "show.json", list: list)
|
render(conn, "show.json", list: list)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/lists/:idOB
|
# GET /api/v1/lists/:id
|
||||||
def show(%{assigns: %{list: list}} = conn, _) do
|
def show(%{assigns: %{list: list}} = conn, _) do
|
||||||
render(conn, "show.json", list: list)
|
render(conn, "show.json", list: list)
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /api/v1/lists/:id
|
# PUT /api/v1/lists/:id
|
||||||
def update(
|
def update(
|
||||||
%{assigns: %{list: list}, private: %{open_api_spex: %{body_params: %{title: title}}}} =
|
%{assigns: %{list: list}, private: %{open_api_spex: %{body_params: params}}} =
|
||||||
conn,
|
conn,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
with {:ok, list} <- Pleroma.List.update(list, params) do
|
||||||
render(conn, "show.json", list: list)
|
render(conn, "show.json", list: list)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin])
|
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin])
|
||||||
|
|
||||||
# Note: scope not present in Mastodon: read:bookmarks
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks)
|
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks)
|
||||||
|
|
||||||
# Note: scope not present in Mastodon: write:bookmarks
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|> User.followed_hashtags()
|
|> User.followed_hashtags()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
excluded_list_members =
|
||||||
|
user
|
||||||
|
|> Pleroma.List.get_exclusive_list_members()
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put(:type, ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|
|
@ -58,7 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|> Map.delete(:local)
|
|> Map.delete(:local)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
[user.ap_id | User.following(user)]
|
[user.ap_id | User.following(user) -- excluded_list_members]
|
||||||
|> ActivityPub.fetch_activities(params)
|
|> ActivityPub.fetch_activities(params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.ListView do
|
||||||
def render("show.json", %{list: list}) do
|
def render("show.json", %{list: list}) do
|
||||||
%{
|
%{
|
||||||
id: to_string(list.id),
|
id: to_string(list.id),
|
||||||
title: list.title
|
title: list.title,
|
||||||
|
exclusive: list.exclusive
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
defmodule Pleroma.Web.OAuth.PasswordController do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
The module contains functions for password reset.
|
The module contains functions for password reset.
|
||||||
"""
|
"""
|
||||||
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.Registration
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
||||||
def request(conn, params) do
|
def request(conn, params) do
|
||||||
nickname_or_email = params["email"] || params["nickname"]
|
nickname_or_email = params["email"] || params["nickname"]
|
||||||
|
|
||||||
TwitterAPI.password_reset(nickname_or_email)
|
Registration.password_reset(nickname_or_email)
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.PasswordView do
|
defmodule Pleroma.Web.OAuth.PasswordView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
import Phoenix.HTML.Form
|
import Phoenix.HTML.Form
|
||||||
alias Pleroma.Web.Gettext
|
alias Pleroma.Web.Gettext
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.Controller do
|
defmodule Pleroma.Web.OAuth.TokenController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.OAuth.TokenView
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.TwitterAPI.TokenView
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.TokenView do
|
defmodule Pleroma.Web.OAuth.TokenView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
def render("index.json", %{tokens: tokens}) do
|
def render("index.json", %{tokens: tokens}) do
|
||||||
tokens
|
tokens
|
||||||
|> render_many(Pleroma.Web.TwitterAPI.TokenView, "show.json")
|
|> render_many(Pleroma.Web.OAuth.TokenView, "show.json")
|
||||||
|> Enum.filter(&Enum.any?/1)
|
|> Enum.filter(&Enum.any?/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -10,10 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderController do
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
# Note: scope not present in Mastodon: read:bookmarks
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :index)
|
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :index)
|
||||||
|
|
||||||
# Note: scope not present in Mastodon: write:bookmarks
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:bookmarks"]} when action in [:create, :update, :delete]
|
%{scopes: ["write:bookmarks"]} when action in [:create, :update, :delete]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.PasswordController do
|
||||||
|
@moduledoc """
|
||||||
|
The module contains functions for password reset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.PasswordResetToken
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Registration
|
||||||
|
|
||||||
|
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||||
|
|
||||||
|
@doc "POST /auth/password"
|
||||||
|
def request(conn, params) do
|
||||||
|
nickname_or_email = params["email"] || params["nickname"]
|
||||||
|
|
||||||
|
Registration.password_reset(nickname_or_email)
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset(conn, %{"token" => token}) do
|
||||||
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
|
false <- PasswordResetToken.expired?(token),
|
||||||
|
%User{} = user <- User.get_cached_by_id(token.user_id) do
|
||||||
|
render(conn, "reset.html", %{
|
||||||
|
token: token,
|
||||||
|
user: user
|
||||||
|
})
|
||||||
|
else
|
||||||
|
_e -> render(conn, "invalid_token.html")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_reset(conn, %{"data" => data}) do
|
||||||
|
with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
|
||||||
|
render(conn, "reset_success.html")
|
||||||
|
else
|
||||||
|
_e -> render(conn, "reset_failed.html")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
59
lib/pleroma/web/pleroma_api/controllers/token_controller.ex
Normal file
59
lib/pleroma/web/pleroma_api/controllers/token_controller.ex
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.TokenController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.PleromaAPI.TokenView
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(:skip_auth when action == :confirm_email)
|
||||||
|
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||||
|
|
||||||
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||||
|
with %User{} = user <- User.get_cached_by_id(uid),
|
||||||
|
true <- user.local and !user.is_confirmed and user.confirmation_token == token,
|
||||||
|
{:ok, _} <- User.confirm(user) do
|
||||||
|
redirect(conn, to: "/")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
with oauth_tokens <- Token.get_user_tokens(user) do
|
||||||
|
conn
|
||||||
|
|> put_view(TokenView)
|
||||||
|
|> render("index.json", %{tokens: oauth_tokens})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
|
||||||
|
Token.delete_user_token(user, id)
|
||||||
|
|
||||||
|
json_reply(conn, 201, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp errors(conn, {:param_cast, _}) do
|
||||||
|
conn
|
||||||
|
|> put_status(400)
|
||||||
|
|> json("Invalid parameters")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp errors(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json("Something went wrong")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp json_reply(conn, status, json) do
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(status, json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,12 +2,11 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.UtilController do
|
defmodule Pleroma.Web.PleromaAPI.UtilController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.Healthcheck
|
alias Pleroma.Healthcheck
|
||||||
|
|
@ -17,19 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.WebFinger
|
|
||||||
|
|
||||||
plug(
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
Pleroma.Web.ApiSpec.CastAndValidate,
|
|
||||||
[replace_params: false]
|
|
||||||
when action != :remote_subscribe and action != :show_subscribe_form
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
Pleroma.Web.Plugs.FederatingPlug
|
|
||||||
when action == :remote_subscribe
|
|
||||||
when action == :show_subscribe_form
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
|
@ -54,125 +42,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaUtilOperation
|
||||||
|
|
||||||
def show_subscribe_form(conn, %{"nickname" => nick}) do
|
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nick),
|
|
||||||
avatar = User.avatar_url(user) do
|
|
||||||
conn
|
|
||||||
|> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
render(conn, "subscribe.html", %{
|
|
||||||
nickname: nick,
|
|
||||||
avatar: nil,
|
|
||||||
error:
|
|
||||||
Pleroma.Web.Gettext.dpgettext(
|
|
||||||
"static_pages",
|
|
||||||
"remote follow error message - user not found",
|
|
||||||
"Could not find user"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_subscribe_form(conn, %{"status_id" => id}) do
|
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
|
||||||
{:ok, ap_id} <- get_ap_id(activity),
|
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.actor),
|
|
||||||
avatar = User.avatar_url(user) do
|
|
||||||
conn
|
|
||||||
|> render("status_interact.html", %{
|
|
||||||
status_link: ap_id,
|
|
||||||
status_id: id,
|
|
||||||
nickname: user.nickname,
|
|
||||||
avatar: avatar,
|
|
||||||
error: false
|
|
||||||
})
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
render(conn, "status_interact.html", %{
|
|
||||||
status_id: id,
|
|
||||||
avatar: nil,
|
|
||||||
error:
|
|
||||||
Pleroma.Web.Gettext.dpgettext(
|
|
||||||
"static_pages",
|
|
||||||
"status interact error message - status not found",
|
|
||||||
"Could not find status"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
|
||||||
show_subscribe_form(conn, %{"nickname" => nick})
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
|
|
||||||
show_subscribe_form(conn, %{"status_id" => id})
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
|
|
||||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
|
||||||
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
|
|
||||||
conn
|
|
||||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
render(conn, "subscribe.html", %{
|
|
||||||
nickname: nick,
|
|
||||||
avatar: nil,
|
|
||||||
error:
|
|
||||||
Pleroma.Web.Gettext.dpgettext(
|
|
||||||
"static_pages",
|
|
||||||
"remote follow error message - unknown error",
|
|
||||||
"Something went wrong."
|
|
||||||
)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
|
|
||||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
|
||||||
%Activity{} = activity <- Activity.get_by_id(id),
|
|
||||||
{:ok, ap_id} <- get_ap_id(activity) do
|
|
||||||
conn
|
|
||||||
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
render(conn, "status_interact.html", %{
|
|
||||||
status_id: id,
|
|
||||||
avatar: nil,
|
|
||||||
error:
|
|
||||||
Pleroma.Web.Gettext.dpgettext(
|
|
||||||
"static_pages",
|
|
||||||
"status interact error message - unknown error",
|
|
||||||
"Something went wrong."
|
|
||||||
)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_interaction(
|
|
||||||
%{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
|
|
||||||
_params
|
|
||||||
) do
|
|
||||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
|
||||||
conn
|
|
||||||
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
|
||||||
else
|
|
||||||
_e -> json(conn, %{error: "Couldn't find user"})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_ap_id(activity) do
|
|
||||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
|
||||||
|
|
||||||
case object do
|
|
||||||
%{data: %{"id" => ap_id}} -> {:ok, ap_id}
|
|
||||||
_ -> {:no_ap_id, nil}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def frontend_configurations(conn, _params) do
|
def frontend_configurations(conn, _params) do
|
||||||
render(conn, "frontend_configurations.json")
|
render(conn, "frontend_configurations.json")
|
||||||
|
|
@ -9,11 +9,13 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
|
|
||||||
def render("show.json", %{folder: %BookmarkFolder{} = folder}) do
|
def render("show.json", %{folder: %BookmarkFolder{} = folder}) do
|
||||||
|
{emoji, emoji_url} = get_emoji(folder.emoji)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: folder.id |> to_string(),
|
id: folder.id |> to_string(),
|
||||||
name: folder.name,
|
name: folder.name,
|
||||||
emoji: folder.emoji,
|
emoji: emoji,
|
||||||
emoji_url: get_emoji_url(folder.emoji)
|
emoji_url: emoji_url
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -21,20 +23,15 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
|
||||||
render_many(folders, __MODULE__, "show.json", Map.delete(opts, :folders))
|
render_many(folders, __MODULE__, "show.json", Map.delete(opts, :folders))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_emoji_url(nil) do
|
defp get_emoji(nil), do: {nil, nil}
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_emoji_url(emoji) do
|
defp get_emoji(emoji) do
|
||||||
if Emoji.unicode?(emoji) do
|
if Emoji.unicode?(emoji) do
|
||||||
nil
|
{emoji, nil}
|
||||||
else
|
else
|
||||||
emoji = Emoji.get(emoji)
|
case Emoji.get(emoji) do
|
||||||
|
nil -> {nil, nil}
|
||||||
if emoji != nil do
|
emoji_data -> {emoji, Emoji.local_url(emoji_data.file)}
|
||||||
Emoji.local_url(emoji.file)
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
13
lib/pleroma/web/pleroma_api/views/util_view.ex
Normal file
13
lib/pleroma/web/pleroma_api/views/util_view.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.UtilView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
def render("frontend_configurations.json", _) do
|
||||||
|
Config.get(:frontend_configurations, %{})
|
||||||
|
|> Enum.into(%{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
import Plug.Conn, only: [put_resp_header: 3]
|
import Plug.Conn, only: [put_resp_header: 3, put_status: 2, send_resp: 3, halt: 1]
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
||||||
|
|
@ -51,10 +51,37 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||||
|> Map.put(:from, from)
|
|> Map.put(:from, from)
|
||||||
|> Map.put(:content_types, false)
|
|> Map.put(:content_types, false)
|
||||||
|
|
||||||
conn = set_content_type(conn, conn.request_path)
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_content_type(conn.request_path)
|
||||||
|
|> Plug.Static.call(opts)
|
||||||
|
|
||||||
# Call Plug.Static with our sanitized content-type
|
if conn.halted do
|
||||||
Plug.Static.call(conn, opts)
|
conn
|
||||||
|
else
|
||||||
|
path = String.trim_leading(conn.request_path, "/")
|
||||||
|
|
||||||
|
if not File.exists?(file_path(path)) do
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> send_404()
|
||||||
|
|> halt()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp send_404(conn) do
|
||||||
|
if String.ends_with?(String.downcase(conn.request_path), ".json") do
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", "application/json")
|
||||||
|
|> send_resp(404, Jason.encode!(%{error: "not found"}))
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", "text/plain")
|
||||||
|
|> send_resp(404, "Not found")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_content_type(conn, "/emoji/" <> filepath) do
|
defp set_content_type(conn, "/emoji/" <> filepath) do
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Config.Holder
|
||||||
|
alias Pleroma.EctoType.Config.RateLimit
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor
|
alias Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor
|
||||||
|
|
||||||
|
|
@ -143,7 +145,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||||
|
|
||||||
def action_settings(plug_opts) do
|
def action_settings(plug_opts) do
|
||||||
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
|
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
|
||||||
limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do
|
{:ok, limits} <- fetch_and_normalize_limits(limiter_name) do
|
||||||
bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name)
|
bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
@ -151,6 +153,72 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||||
limits: limits,
|
limits: limits,
|
||||||
opts: plug_opts
|
opts: plug_opts
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
:disabled -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_and_normalize_limits(limiter_name) do
|
||||||
|
limits = Config.get([:rate_limit, limiter_name])
|
||||||
|
|
||||||
|
case normalize_limits(limits) do
|
||||||
|
{:ok, limits} ->
|
||||||
|
{:ok, limits}
|
||||||
|
|
||||||
|
:disabled ->
|
||||||
|
:disabled
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
default_limits =
|
||||||
|
Holder.default_config(:pleroma, :rate_limit)
|
||||||
|
|> get_default_limits(limiter_name)
|
||||||
|
|
||||||
|
case normalize_limits(default_limits) do
|
||||||
|
{:ok, normalized_limits} ->
|
||||||
|
warn_invalid_limits_once(limiter_name, limits)
|
||||||
|
{:ok, normalized_limits}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
warn_invalid_limits_once(limiter_name, limits)
|
||||||
|
:disabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_default_limits(%{} = rate_limit, limiter_name), do: Map.get(rate_limit, limiter_name)
|
||||||
|
|
||||||
|
defp get_default_limits(rate_limit, limiter_name) when is_list(rate_limit) do
|
||||||
|
if Keyword.keyword?(rate_limit) do
|
||||||
|
Keyword.get(rate_limit, limiter_name)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_default_limits(_, _), do: nil
|
||||||
|
|
||||||
|
@invalid_limits_warned_key {__MODULE__, :invalid_limits_warned}
|
||||||
|
|
||||||
|
defp warn_invalid_limits_once(limiter_name, limits) do
|
||||||
|
warned = :persistent_term.get(@invalid_limits_warned_key, MapSet.new())
|
||||||
|
|
||||||
|
if MapSet.member?(warned, limiter_name) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
:persistent_term.put(@invalid_limits_warned_key, MapSet.put(warned, limiter_name))
|
||||||
|
|
||||||
|
Logger.warning(
|
||||||
|
"Invalid rate limiter config for #{inspect(limiter_name)}: #{inspect(limits)}. Falling back to defaults or disabling this limiter."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_limits(nil), do: :disabled
|
||||||
|
|
||||||
|
defp normalize_limits(limits) do
|
||||||
|
case RateLimit.cast(limits) do
|
||||||
|
{:ok, normalized_limits} -> {:ok, normalized_limits}
|
||||||
|
:error -> :error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
defmodule Pleroma.Web.Preload.Providers.Instance do
|
defmodule Pleroma.Web.Preload.Providers.Instance do
|
||||||
alias Pleroma.Web.MastodonAPI.InstanceView
|
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||||
alias Pleroma.Web.Nodeinfo.Nodeinfo
|
alias Pleroma.Web.Nodeinfo.Nodeinfo
|
||||||
|
alias Pleroma.Web.PleromaAPI.UtilView
|
||||||
alias Pleroma.Web.Plugs.InstanceStatic
|
alias Pleroma.Web.Plugs.InstanceStatic
|
||||||
alias Pleroma.Web.Preload.Providers.Provider
|
alias Pleroma.Web.Preload.Providers.Provider
|
||||||
alias Pleroma.Web.TwitterAPI.UtilView
|
|
||||||
|
|
||||||
@behaviour Provider
|
@behaviour Provider
|
||||||
@instance_url "/api/v1/instance"
|
@instance_url "/api/v1/instance"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
defmodule Pleroma.Web.Registration do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
alias Pleroma.Emails.Mailer
|
alias Pleroma.Emails.Mailer
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
defmodule Pleroma.Web.RemoteInteraction.RemoteInteractionController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
@ -14,18 +14,27 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||||
alias Pleroma.Web.Auth.TOTPAuthenticator
|
alias Pleroma.Web.Auth.TOTPAuthenticator
|
||||||
alias Pleroma.Web.Auth.WrapperAuthenticator
|
alias Pleroma.Web.Auth.WrapperAuthenticator
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.WebFinger
|
||||||
|
|
||||||
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
||||||
|
|
||||||
|
plug(
|
||||||
|
Pleroma.Web.ApiSpec.CastAndValidate,
|
||||||
|
[replace_params: false]
|
||||||
|
when action == :remote_interaction
|
||||||
|
)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.FederatingPlug)
|
plug(Pleroma.Web.Plugs.FederatingPlug)
|
||||||
|
|
||||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
||||||
plug(
|
plug(
|
||||||
Pleroma.Web.Plugs.OAuthScopesPlug,
|
Pleroma.Web.Plugs.OAuthScopesPlug,
|
||||||
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
||||||
when action in [:do_follow]
|
when action == :do_follow
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.RemoteInteractionOperation
|
||||||
|
|
||||||
# GET /ostatus_subscribe
|
# GET /ostatus_subscribe
|
||||||
#
|
#
|
||||||
def follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
|
def follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
|
||||||
|
|
@ -125,7 +134,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||||
#
|
#
|
||||||
def authorize_interaction(conn, %{"uri" => uri}) do
|
def authorize_interaction(conn, %{"uri" => uri}) do
|
||||||
conn
|
conn
|
||||||
|> redirect(to: Routes.remote_follow_path(conn, :follow, %{acct: uri}))
|
|> redirect(to: Routes.remote_interaction_path(conn, :follow, %{acct: uri}))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
|
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
|
||||||
|
|
@ -162,4 +171,122 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||||
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||||
render(conn, "followed.html", %{error: "Something went wrong."})
|
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show_subscribe_form(conn, %{"nickname" => nick}) do
|
||||||
|
with %User{} = user <- User.get_cached_by_nickname(nick),
|
||||||
|
avatar = User.avatar_url(user) do
|
||||||
|
conn
|
||||||
|
|> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
render(conn, "subscribe.html", %{
|
||||||
|
nickname: nick,
|
||||||
|
avatar: nil,
|
||||||
|
error:
|
||||||
|
Pleroma.Web.Gettext.dpgettext(
|
||||||
|
"static_pages",
|
||||||
|
"remote follow error message - user not found",
|
||||||
|
"Could not find user"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_subscribe_form(conn, %{"status_id" => id}) do
|
||||||
|
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||||
|
{:ok, ap_id} <- get_ap_id(activity),
|
||||||
|
%User{} = user <- User.get_cached_by_ap_id(activity.actor),
|
||||||
|
avatar = User.avatar_url(user) do
|
||||||
|
conn
|
||||||
|
|> render("status_interact.html", %{
|
||||||
|
status_link: ap_id,
|
||||||
|
status_id: id,
|
||||||
|
nickname: user.nickname,
|
||||||
|
avatar: avatar,
|
||||||
|
error: false
|
||||||
|
})
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
render(conn, "status_interact.html", %{
|
||||||
|
status_id: id,
|
||||||
|
avatar: nil,
|
||||||
|
error:
|
||||||
|
Pleroma.Web.Gettext.dpgettext(
|
||||||
|
"static_pages",
|
||||||
|
"status interact error message - status not found",
|
||||||
|
"Could not find status"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
||||||
|
show_subscribe_form(conn, %{"nickname" => nick})
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
|
||||||
|
show_subscribe_form(conn, %{"status_id" => id})
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
|
||||||
|
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||||
|
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
|
||||||
|
conn
|
||||||
|
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
render(conn, "subscribe.html", %{
|
||||||
|
nickname: nick,
|
||||||
|
avatar: nil,
|
||||||
|
error:
|
||||||
|
Pleroma.Web.Gettext.dpgettext(
|
||||||
|
"static_pages",
|
||||||
|
"remote follow error message - unknown error",
|
||||||
|
"Something went wrong."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
|
||||||
|
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
|
||||||
|
%Activity{} = activity <- Activity.get_by_id(id),
|
||||||
|
{:ok, ap_id} <- get_ap_id(activity) do
|
||||||
|
conn
|
||||||
|
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
render(conn, "status_interact.html", %{
|
||||||
|
status_id: id,
|
||||||
|
avatar: nil,
|
||||||
|
error:
|
||||||
|
Pleroma.Web.Gettext.dpgettext(
|
||||||
|
"static_pages",
|
||||||
|
"status interact error message - unknown error",
|
||||||
|
"Something went wrong."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_interaction(
|
||||||
|
%{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
|
||||||
|
_params
|
||||||
|
) do
|
||||||
|
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
||||||
|
conn
|
||||||
|
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
||||||
|
else
|
||||||
|
_e -> json(conn, %{error: "Couldn't find user"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_ap_id(activity) do
|
||||||
|
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
case object do
|
||||||
|
%{data: %{"id" => ap_id}} -> {:ok, ap_id}
|
||||||
|
_ -> {:no_ap_id, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do
|
defmodule Pleroma.Web.RemoteInteraction.RemoteInteractionView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
import Phoenix.HTML
|
||||||
import Phoenix.HTML.Form
|
import Phoenix.HTML.Form
|
||||||
|
import Phoenix.HTML.Link
|
||||||
alias Pleroma.Web.Gettext
|
alias Pleroma.Web.Gettext
|
||||||
|
|
||||||
def avatar_url(user) do
|
def avatar_url(user) do
|
||||||
|
|
@ -226,23 +226,28 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.OAuth do
|
||||||
pipe_through(:pleroma_api)
|
pipe_through(:pleroma_api)
|
||||||
|
|
||||||
get("/password_reset/:token", PasswordController, :reset, as: :reset_password)
|
get("/password_reset/:token", PasswordController, :reset, as: :reset_password)
|
||||||
post("/password_reset", PasswordController, :do_reset, as: :reset_password)
|
post("/password_reset", PasswordController, :do_reset, as: :reset_password)
|
||||||
get("/emoji", UtilController, :emoji)
|
|
||||||
get("/captcha", UtilController, :captcha)
|
|
||||||
get("/healthcheck", UtilController, :healthcheck)
|
|
||||||
post("/remote_interaction", UtilController, :remote_interaction)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:pleroma_api)
|
pipe_through(:pleroma_api)
|
||||||
|
|
||||||
|
get("/emoji", UtilController, :emoji)
|
||||||
|
get("/captcha", UtilController, :captcha)
|
||||||
|
get("/healthcheck", UtilController, :healthcheck)
|
||||||
get("/federation_status", InstancesController, :show)
|
get("/federation_status", InstancesController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/api/v1/pleroma", Pleroma.Web.RemoteInteraction do
|
||||||
|
pipe_through(:pleroma_api)
|
||||||
|
|
||||||
|
post("/remote_interaction", RemoteInteractionController, :remote_interaction)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma", Pleroma.Web do
|
scope "/api/v1/pleroma", Pleroma.Web do
|
||||||
pipe_through(:pleroma_api)
|
pipe_through(:pleroma_api)
|
||||||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||||
|
|
@ -484,18 +489,18 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.TwitterAPI do
|
scope "/", Pleroma.Web.RemoteInteraction do
|
||||||
pipe_through(:pleroma_html)
|
pipe_through(:pleroma_html)
|
||||||
|
|
||||||
post("/main/ostatus", UtilController, :remote_subscribe)
|
post("/main/ostatus", RemoteInteractionController, :remote_subscribe)
|
||||||
get("/main/ostatus", UtilController, :show_subscribe_form)
|
get("/main/ostatus", RemoteInteractionController, :show_subscribe_form)
|
||||||
get("/ostatus_subscribe", RemoteFollowController, :follow)
|
get("/ostatus_subscribe", RemoteInteractionController, :follow)
|
||||||
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
|
post("/ostatus_subscribe", RemoteInteractionController, :do_follow)
|
||||||
|
|
||||||
get("/authorize_interaction", RemoteFollowController, :authorize_interaction)
|
get("/authorize_interaction", RemoteInteractionController, :authorize_interaction)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
scope "/api/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
post("/change_email", UtilController, :change_email)
|
post("/change_email", UtilController, :change_email)
|
||||||
|
|
@ -853,7 +858,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
pipe_through(:config)
|
pipe_through(:config)
|
||||||
|
|
||||||
get("/pleroma/frontend_configurations", TwitterAPI.UtilController, :frontend_configurations)
|
get("/pleroma/frontend_configurations", PleromaAPI.UtilController, :frontend_configurations)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
|
|
@ -861,7 +866,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get(
|
get(
|
||||||
"/account/confirm_email/:user_id/:token",
|
"/account/confirm_email/:user_id/:token",
|
||||||
TwitterAPI.Controller,
|
OAuth.TokenController,
|
||||||
:confirm_email,
|
:confirm_email,
|
||||||
as: :confirm_email
|
as: :confirm_email
|
||||||
)
|
)
|
||||||
|
|
@ -873,11 +878,11 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web, as: :authenticated_twitter_api do
|
scope "/api", Pleroma.Web, as: :authenticated_pleroma_api do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
|
get("/oauth_tokens", OAuth.TokenController, :oauth_tokens)
|
||||||
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
|
delete("/oauth_tokens/:id", OAuth.TokenController, :revoke_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
|
|
@ -1026,7 +1031,9 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through(:pleroma_html)
|
pipe_through(:pleroma_html)
|
||||||
|
|
||||||
post("/auth/password", TwitterAPI.PasswordController, :request)
|
post("/auth/password", OAuth.PasswordController, :request)
|
||||||
|
|
||||||
|
get("/embed/:id", EmbedController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web do
|
scope "/proxy/", Pleroma.Web do
|
||||||
|
|
@ -1088,7 +1095,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
||||||
match(:*, "/api/pleroma/*path", LegacyPleromaApiRerouterPlug, [])
|
match(:*, "/api/pleroma/*path", LegacyPleromaApiRerouterPlug, [])
|
||||||
get("/api/*path", RedirectController, :api_not_implemented)
|
get("/api/*path", RedirectController, :api_not_implemented)
|
||||||
get("/phoenix/live_dashboard", RedirectController, :live_dashboard)
|
get("/phoenix/live_dashboard/*path", RedirectController, :live_dashboard)
|
||||||
get("/*path", RedirectController, :redirector_with_preload)
|
get("/*path", RedirectController, :redirector_with_preload)
|
||||||
|
|
||||||
options("/*path", RedirectController, :empty)
|
options("/*path", RedirectController, :empty)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %></h2>
|
||||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||||
<p><%= @followee.nickname %></p>
|
<p><%= @followee.nickname %></p>
|
||||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %>
|
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "user"], fn f -> %>
|
||||||
<%= hidden_input f, :id, value: @followee.id %>
|
<%= hidden_input f, :id, value: @followee.id %>
|
||||||
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %>
|
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %></h2>
|
||||||
<p><%= @followee.nickname %></p>
|
<p><%= @followee.nickname %></p>
|
||||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
|
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
|
||||||
<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %>
|
<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %>
|
||||||
<br>
|
<br>
|
||||||
<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %>
|
<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %></h2>
|
||||||
<p><%= @followee.nickname %></p>
|
<p><%= @followee.nickname %></p>
|
||||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||||
<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
|
<%= form_for @conn, Routes.remote_interaction_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
|
||||||
<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %>
|
<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %>
|
||||||
<br>
|
<br>
|
||||||
<%= hidden_input f, :id, value: @followee.id %>
|
<%= hidden_input f, :id, value: @followee.id %>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %></h2>
|
||||||
<% else %>
|
<% else %>
|
||||||
<h2><%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %></h2>
|
<h2><%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %></h2>
|
||||||
<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %>
|
<%= form_for @conn, Routes.remote_interaction_path(@conn, :remote_subscribe), [as: "status"], fn f -> %>
|
||||||
<%= hidden_input f, :status_id, value: @status_id %>
|
<%= hidden_input f, :status_id, value: @status_id %>
|
||||||
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
||||||
<%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %>
|
<%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %></h2>
|
||||||
<% else %>
|
<% else %>
|
||||||
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %></h2>
|
<h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %></h2>
|
||||||
<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
|
<%= form_for @conn, Routes.remote_interaction_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
|
||||||
<%= hidden_input f, :nickname, value: @nickname %>
|
<%= hidden_input f, :nickname, value: @nickname %>
|
||||||
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
<%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
|
||||||
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %>
|
<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %>
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<h1><%= link instance_name(), to: "/" %></h1>
|
<h1><%= link instance_name(), to: "/" %></h1>
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<form class="pull-right collapse" method="POST" action="<%= Helpers.util_path(@conn, :remote_subscribe) %>">
|
<form class="pull-right collapse" method="POST" action="<%= Helpers.remote_interaction_path(@conn, :remote_subscribe) %>">
|
||||||
<input type="hidden" name="nickname" value="<%= @user.nickname %>">
|
<input type="hidden" name="nickname" value="<%= @user.nickname %>">
|
||||||
<input type="hidden" name="profile" value="">
|
<input type="hidden" name="profile" value="">
|
||||||
<button type="submit" class="collapse"><%= Gettext.dpgettext("static_pages", "static fe profile page remote follow button", "Remote follow") %></button>
|
<button type="submit" class="collapse"><%= Gettext.dpgettext("static_pages", "static fe profile page remote follow button", "Remote follow") %></button>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.UtilView do
|
|
||||||
use Pleroma.Web, :view
|
|
||||||
import Phoenix.HTML
|
|
||||||
import Phoenix.HTML.Form
|
|
||||||
import Phoenix.HTML.Link
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Web.Endpoint
|
|
||||||
alias Pleroma.Web.Gettext
|
|
||||||
|
|
||||||
def status_net_config(instance) do
|
|
||||||
"""
|
|
||||||
<config>
|
|
||||||
<site>
|
|
||||||
<name>#{Keyword.get(instance, :name)}</name>
|
|
||||||
<site>#{Endpoint.url()}</site>
|
|
||||||
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
|
||||||
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
|
||||||
</site>
|
|
||||||
</config>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("frontend_configurations.json", _) do
|
|
||||||
Config.get(:frontend_configurations, %{})
|
|
||||||
|> Enum.into(%{})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -23,10 +23,12 @@ defmodule Pleroma.Repo.Migrations.MoveActivityExpirationsToOban do
|
||||||
|> Pleroma.Repo.stream()
|
|> Pleroma.Repo.stream()
|
||||||
|> Stream.each(fn expiration ->
|
|> Stream.each(fn expiration ->
|
||||||
with {:ok, expires_at} <- DateTime.from_naive(expiration.scheduled_at, "Etc/UTC") do
|
with {:ok, expires_at} <- DateTime.from_naive(expiration.scheduled_at, "Etc/UTC") do
|
||||||
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
|
Pleroma.Workers.PurgeExpiredActivity.enqueue(
|
||||||
activity_id: FlakeId.to_string(expiration.activity_id),
|
%{
|
||||||
expires_at: expires_at
|
activity_id: FlakeId.to_string(expiration.activity_id)
|
||||||
})
|
},
|
||||||
|
scheduled_at: expires_at
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ defmodule Pleroma.Repo.Migrations.MoveTokensExpirationIntoOban do
|
||||||
from(t in Pleroma.Web.OAuth.Token, where: t.valid_until > ^NaiveDateTime.utc_now())
|
from(t in Pleroma.Web.OAuth.Token, where: t.valid_until > ^NaiveDateTime.utc_now())
|
||||||
|> Pleroma.Repo.stream()
|
|> Pleroma.Repo.stream()
|
||||||
|> Stream.each(fn token ->
|
|> Stream.each(fn token ->
|
||||||
Pleroma.Workers.PurgeExpiredToken.enqueue(%{
|
enqueue(%{
|
||||||
token_id: token.id,
|
token_id: token.id,
|
||||||
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
||||||
mod: Pleroma.Web.OAuth.Token
|
mod: Pleroma.Web.OAuth.Token
|
||||||
|
|
@ -33,7 +33,7 @@ defmodule Pleroma.Repo.Migrations.MoveTokensExpirationIntoOban do
|
||||||
from(t in Pleroma.MFA.Token, where: t.valid_until > ^NaiveDateTime.utc_now())
|
from(t in Pleroma.MFA.Token, where: t.valid_until > ^NaiveDateTime.utc_now())
|
||||||
|> Pleroma.Repo.stream()
|
|> Pleroma.Repo.stream()
|
||||||
|> Stream.each(fn token ->
|
|> Stream.each(fn token ->
|
||||||
Pleroma.Workers.PurgeExpiredToken.enqueue(%{
|
enqueue(%{
|
||||||
token_id: token.id,
|
token_id: token.id,
|
||||||
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
||||||
mod: Pleroma.MFA.Token
|
mod: Pleroma.MFA.Token
|
||||||
|
|
@ -41,4 +41,14 @@ defmodule Pleroma.Repo.Migrations.MoveTokensExpirationIntoOban do
|
||||||
end)
|
end)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec enqueue(%{token_id: integer(), valid_until: DateTime.t()}) ::
|
||||||
|
{:ok, Oban.Job.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
defp enqueue(args) do
|
||||||
|
{scheduled_at, args} = Map.pop(args, :valid_until)
|
||||||
|
|
||||||
|
args
|
||||||
|
|> Pleroma.Workers.PurgeExpiredToken.new(scheduled_at: scheduled_at)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddExclusiveToLists do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:lists) do
|
||||||
|
add(:exclusive, :boolean, default: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
1
test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json
vendored
Normal file
1
test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -174,6 +174,22 @@ defmodule Pleroma.ConfigDBTest do
|
||||||
assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
|
assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
|
||||||
assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
|
assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "rejects invalid :rate_limit values (e.g. empty-string scale from AdminFE)" do
|
||||||
|
assert {:error, _changeset} =
|
||||||
|
ConfigDB.update_or_create(%{
|
||||||
|
group: ":pleroma",
|
||||||
|
key: ":rate_limit",
|
||||||
|
value: [
|
||||||
|
%{
|
||||||
|
"tuple" => [
|
||||||
|
":statuses_actions",
|
||||||
|
[%{"tuple" => ["", 0]}, %{"tuple" => ["", ""]}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete/1" do
|
describe "delete/1" do
|
||||||
|
|
@ -273,24 +289,28 @@ defmodule Pleroma.ConfigDBTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "sigil" do
|
test "sigil" do
|
||||||
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
|
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source ==
|
||||||
|
~r/comp[lL][aA][iI][nN]er/.source
|
||||||
end
|
end
|
||||||
|
|
||||||
test "link sigil" do
|
test "link sigil" do
|
||||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
|
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source ==
|
||||||
|
~r/https:\/\/example.com/.source
|
||||||
end
|
end
|
||||||
|
|
||||||
test "link sigil with um modifiers" do
|
test "link sigil with um modifiers" do
|
||||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
|
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um").source ==
|
||||||
~r/https:\/\/example.com/um
|
~r/https:\/\/example.com/um.source
|
||||||
end
|
end
|
||||||
|
|
||||||
test "link sigil with i modifier" do
|
test "link sigil with i modifier" do
|
||||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
|
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source ==
|
||||||
|
~r/https:\/\/example.com/i.source
|
||||||
end
|
end
|
||||||
|
|
||||||
test "link sigil with s modifier" do
|
test "link sigil with s modifier" do
|
||||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
|
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source ==
|
||||||
|
~r/https:\/\/example.com/s.source
|
||||||
end
|
end
|
||||||
|
|
||||||
test "raise if valid delimiter not found" do
|
test "raise if valid delimiter not found" do
|
||||||
|
|
@ -460,11 +480,11 @@ defmodule Pleroma.ConfigDBTest do
|
||||||
test "complex keyword with sigil" do
|
test "complex keyword with sigil" do
|
||||||
assert ConfigDB.to_elixir_types([
|
assert ConfigDB.to_elixir_types([
|
||||||
%{"tuple" => [":federated_timeline_removal", []]},
|
%{"tuple" => [":federated_timeline_removal", []]},
|
||||||
%{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
|
%{"tuple" => [":reject", [~r/comp[lL][aA][iI][nN]er/.source]]},
|
||||||
%{"tuple" => [":replace", []]}
|
%{"tuple" => [":replace", []]}
|
||||||
]) == [
|
]) == [
|
||||||
federated_timeline_removal: [],
|
federated_timeline_removal: [],
|
||||||
reject: [~r/comp[lL][aA][iI][nN]er/],
|
reject: [~r/comp[lL][aA][iI][nN]er/.source],
|
||||||
replace: []
|
replace: []
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,49 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy follows nested redirects via proxy", %{
|
||||||
|
tls_server: tls_server,
|
||||||
|
proxy: proxy
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/nested_redirect"
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
pool: :media,
|
||||||
|
proxy: proxy.proxy_url,
|
||||||
|
insecure: true,
|
||||||
|
connect_timeout: 1_000,
|
||||||
|
recv_timeout: 1_000,
|
||||||
|
follow_redirect: true
|
||||||
|
]
|
||||||
|
|
||||||
|
assert {:ok, 200, _headers, ref} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
|
||||||
|
assert collect_body(ref) == "ok"
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy stops following redirects after limit is reached", %{
|
||||||
|
tls_server: tls_server,
|
||||||
|
proxy: proxy
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/infinite_redirect"
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
pool: :media,
|
||||||
|
proxy: proxy.proxy_url,
|
||||||
|
insecure: true,
|
||||||
|
connect_timeout: 1_000,
|
||||||
|
recv_timeout: 1_000,
|
||||||
|
follow_redirect: true
|
||||||
|
]
|
||||||
|
|
||||||
|
assert {:ok, 302, _headers, ref} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||||
|
end
|
||||||
|
|
||||||
defp collect_body(ref, acc \\ "") do
|
defp collect_body(ref, acc \\ "") do
|
||||||
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
||||||
:done -> acc
|
:done -> acc
|
||||||
|
|
@ -148,6 +191,12 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
||||||
{:ok, path} <- parse_path(data) do
|
{:ok, path} <- parse_path(data) do
|
||||||
case path do
|
case path do
|
||||||
|
"/infinite_redirect" ->
|
||||||
|
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/infinite_redirect"}], "")
|
||||||
|
|
||||||
|
"/nested_redirect" ->
|
||||||
|
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/redirect"}], "")
|
||||||
|
|
||||||
"/redirect" ->
|
"/redirect" ->
|
||||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/final"}], "")
|
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/final"}], "")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
test "accepts the 'list' stream", %{token: token, user: user} do
|
test "accepts the 'list' stream", %{token: token, user: user} do
|
||||||
posting_user = insert(:user)
|
posting_user = insert(:user)
|
||||||
|
|
||||||
{:ok, list} = Pleroma.List.create("test", user)
|
{:ok, list} = Pleroma.List.create(%{title: "test"}, user)
|
||||||
Pleroma.List.follow(list, posting_user)
|
Pleroma.List.follow(list, posting_user)
|
||||||
|
|
||||||
assert {:ok, _} = start_socket("?stream=list&access_token=#{token.token}&list=#{list.id}")
|
assert {:ok, _} = start_socket("?stream=list&access_token=#{token.token}&list=#{list.id}")
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,23 @@ defmodule Pleroma.ListTest do
|
||||||
|
|
||||||
test "creating a list" do
|
test "creating a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
%Pleroma.List{title: title} = Pleroma.List.get(list.id, user)
|
%Pleroma.List{title: title, exclusive: exclusive} = Pleroma.List.get(list.id, user)
|
||||||
assert title == "title"
|
assert title == "title"
|
||||||
|
assert exclusive == false
|
||||||
end
|
end
|
||||||
|
|
||||||
test "validates title" do
|
test "validates title" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
assert {:error, changeset} = Pleroma.List.create("", user)
|
assert {:error, changeset} = Pleroma.List.create(%{title: ""}, user)
|
||||||
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "getting a list not belonging to the user" do
|
test "getting a list not belonging to the user" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
ret = Pleroma.List.get(list.id, other_user)
|
ret = Pleroma.List.get(list.id, other_user)
|
||||||
assert is_nil(ret)
|
assert is_nil(ret)
|
||||||
end
|
end
|
||||||
|
|
@ -33,7 +34,7 @@ defmodule Pleroma.ListTest do
|
||||||
test "adding an user to a list" do
|
test "adding an user to a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("title", user)
|
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, %{following: following}} = Pleroma.List.follow(list, other_user)
|
{:ok, %{following: following}} = Pleroma.List.follow(list, other_user)
|
||||||
assert [other_user.follower_address] == following
|
assert [other_user.follower_address] == following
|
||||||
end
|
end
|
||||||
|
|
@ -41,7 +42,7 @@ defmodule Pleroma.ListTest do
|
||||||
test "removing an user from a list" do
|
test "removing an user from a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("title", user)
|
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
|
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
|
||||||
{:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user)
|
{:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user)
|
||||||
assert [] == following
|
assert [] == following
|
||||||
|
|
@ -49,14 +50,27 @@ defmodule Pleroma.ListTest do
|
||||||
|
|
||||||
test "renaming a list" do
|
test "renaming a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("title", user)
|
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, %{title: title}} = Pleroma.List.rename(list, "new")
|
{:ok, %{title: title}} = Pleroma.List.update(list, %{title: "new"})
|
||||||
assert "new" == title
|
assert "new" == title
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updating a list exclusivity" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, %{exclusive: exclusive} = list} =
|
||||||
|
Pleroma.List.create(%{title: "title", exclusive: true}, user)
|
||||||
|
|
||||||
|
assert exclusive == true
|
||||||
|
{:ok, %{exclusive: exclusive} = list} = Pleroma.List.update(list, %{exclusive: false})
|
||||||
|
assert exclusive == false
|
||||||
|
{:ok, %{exclusive: exclusive}} = Pleroma.List.update(list, %{exclusive: true})
|
||||||
|
assert exclusive == true
|
||||||
|
end
|
||||||
|
|
||||||
test "deleting a list" do
|
test "deleting a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("title", user)
|
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, list} = Pleroma.List.delete(list)
|
{:ok, list} = Pleroma.List.delete(list)
|
||||||
assert is_nil(Repo.get(Pleroma.List, list.id))
|
assert is_nil(Repo.get(Pleroma.List, list.id))
|
||||||
end
|
end
|
||||||
|
|
@ -65,7 +79,7 @@ defmodule Pleroma.ListTest do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
third_user = insert(:user)
|
third_user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("title", user)
|
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||||
{:ok, following} = Pleroma.List.get_following(list)
|
{:ok, following} = Pleroma.List.get_following(list)
|
||||||
|
|
@ -76,9 +90,9 @@ defmodule Pleroma.ListTest do
|
||||||
test "getting all lists by an user" do
|
test "getting all lists by an user" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||||
lists = Pleroma.List.for_user(user, %{})
|
lists = Pleroma.List.for_user(user, %{})
|
||||||
assert list_one in lists
|
assert list_one in lists
|
||||||
assert list_two in lists
|
assert list_two in lists
|
||||||
|
|
@ -88,9 +102,9 @@ defmodule Pleroma.ListTest do
|
||||||
test "getting all lists the user is a member of" do
|
test "getting all lists the user is a member of" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||||
{:ok, list_one} = Pleroma.List.follow(list_one, other_user)
|
{:ok, list_one} = Pleroma.List.follow(list_one, other_user)
|
||||||
{:ok, list_two} = Pleroma.List.follow(list_two, other_user)
|
{:ok, list_two} = Pleroma.List.follow(list_two, other_user)
|
||||||
{:ok, list_three} = Pleroma.List.follow(list_three, user)
|
{:ok, list_three} = Pleroma.List.follow(list_three, user)
|
||||||
|
|
@ -106,8 +120,8 @@ defmodule Pleroma.ListTest do
|
||||||
not_owner = insert(:user)
|
not_owner = insert(:user)
|
||||||
member_1 = insert(:user)
|
member_1 = insert(:user)
|
||||||
member_2 = insert(:user)
|
member_2 = insert(:user)
|
||||||
{:ok, owned_list} = Pleroma.List.create("owned", owner)
|
{:ok, owned_list} = Pleroma.List.create(%{title: "owned"}, owner)
|
||||||
{:ok, not_owned_list} = Pleroma.List.create("not owned", not_owner)
|
{:ok, not_owned_list} = Pleroma.List.create(%{title: "not owned"}, not_owner)
|
||||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_1)
|
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_1)
|
||||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_2)
|
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_2)
|
||||||
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1)
|
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1)
|
||||||
|
|
@ -123,14 +137,14 @@ defmodule Pleroma.ListTest do
|
||||||
|
|
||||||
test "get by ap_id" do
|
test "get by ap_id" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
assert Pleroma.List.get_by_ap_id(list.ap_id) == list
|
assert Pleroma.List.get_by_ap_id(list.ap_id) == list
|
||||||
end
|
end
|
||||||
|
|
||||||
test "memberships" do
|
test "memberships" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
member = insert(:user)
|
member = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, member)
|
{:ok, list} = Pleroma.List.follow(list, member)
|
||||||
|
|
||||||
assert Pleroma.List.memberships(member) == [list.ap_id]
|
assert Pleroma.List.memberships(member) == [list.ap_id]
|
||||||
|
|
@ -140,7 +154,7 @@ defmodule Pleroma.ListTest do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
member = insert(:user)
|
member = insert(:user)
|
||||||
|
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, member)
|
{:ok, list} = Pleroma.List.follow(list, member)
|
||||||
|
|
||||||
assert Pleroma.List.member?(list, member)
|
assert Pleroma.List.member?(list, member)
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,12 @@ defmodule Pleroma.MarkerTest do
|
||||||
insert(:notification, user: user, activity: insert(:note_activity))
|
insert(:notification, user: user, activity: insert(:note_activity))
|
||||||
insert(:notification, user: user, activity: insert(:note_activity))
|
insert(:notification, user: user, activity: insert(:note_activity))
|
||||||
insert(:marker, timeline: "home", user: user)
|
insert(:marker, timeline: "home", user: user)
|
||||||
|
%Marker{} = refreshed_marker = refresh_record(marker)
|
||||||
|
|
||||||
assert Marker.get_markers(
|
assert Marker.get_markers(
|
||||||
user,
|
user,
|
||||||
["notifications"]
|
["notifications"]
|
||||||
) == [%Marker{refresh_record(marker) | unread_count: 2}]
|
) == [%{refreshed_marker | unread_count: 2}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ defmodule Pleroma.RepoTest do
|
||||||
describe "get_assoc/2" do
|
describe "get_assoc/2" do
|
||||||
test "get assoc from preloaded data" do
|
test "get assoc from preloaded data" do
|
||||||
user = %User{name: "Agent Smith"}
|
user = %User{name: "Agent Smith"}
|
||||||
token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user}
|
%Pleroma.Web.OAuth.Token{} = token = insert(:oauth_token)
|
||||||
|
token = %{token | user: user}
|
||||||
assert Repo.get_assoc(token, :user) == {:ok, user}
|
assert Repo.get_assoc(token, :user) == {:ok, user}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,29 +74,6 @@ defmodule Pleroma.Search.MeilisearchTest do
|
||||||
assert_received("posted_to_meilisearch")
|
assert_received("posted_to_meilisearch")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "doesn't index posts that are not public" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
Enum.each(["private", "direct"], fn visibility ->
|
|
||||||
{:ok, activity} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
status: "guys i just don't wanna leave the swamp",
|
|
||||||
visibility: visibility
|
|
||||||
})
|
|
||||||
|
|
||||||
args = %{"op" => "add_to_index", "activity" => activity.id}
|
|
||||||
|
|
||||||
Config
|
|
||||||
|> expect(:get, fn
|
|
||||||
[Pleroma.Search, :module], nil ->
|
|
||||||
Meilisearch
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
|
||||||
assert :ok = perform_job(SearchIndexingWorker, args)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deletes posts from index when deleted locally" do
|
test "deletes posts from index when deleted locally" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
||||||
69
test/pleroma/search_test.exs
Normal file
69
test/pleroma/search_test.exs
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.SearchTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Workers.SearchIndexingWorker
|
||||||
|
|
||||||
|
test "indexes posts that are public" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Well this is a story all about how my life got flipped turned upside down",
|
||||||
|
visibility: "public"
|
||||||
|
})
|
||||||
|
|
||||||
|
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||||
|
|
||||||
|
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't index posts that are not public" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
Enum.each(["private", "direct"], fn visibility ->
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "guys i just don't wanna leave the swamp",
|
||||||
|
visibility: visibility
|
||||||
|
})
|
||||||
|
|
||||||
|
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||||
|
|
||||||
|
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Indexes appropriate activity types" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "I'm my own hype man",
|
||||||
|
visibility: "public"
|
||||||
|
})
|
||||||
|
|
||||||
|
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||||
|
|
||||||
|
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
||||||
|
|
||||||
|
{:ok, fav_activity} = CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
args = %{"op" => "add_to_index", "activity" => fav_activity.id}
|
||||||
|
|
||||||
|
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||||
|
|
||||||
|
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
|
args = %{"op" => "add_to_index", "activity" => repeat_activity.id}
|
||||||
|
|
||||||
|
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -499,6 +499,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
"https://queef.in/storage/banner.gif"
|
"https://queef.in/storage/banner.gif"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "works with alsoKnownAs as string" do
|
||||||
|
user_id = "https://hub.netzgemeinde.eu/channel/jupiter_rowland"
|
||||||
|
|
||||||
|
user_data =
|
||||||
|
"test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json"
|
||||||
|
|> File.read!()
|
||||||
|
|
||||||
|
user_data_decoded =
|
||||||
|
user_data
|
||||||
|
|> Jason.decode!()
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: ^user_id
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: user_data,
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||||
|
|
||||||
|
assert is_list(user.also_known_as)
|
||||||
|
assert user.also_known_as == [user_data_decoded["alsoKnownAs"]]
|
||||||
|
end
|
||||||
|
|
||||||
test "it fetches the appropriate tag-restricted posts" do
|
test "it fetches the appropriate tag-restricted posts" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
@ -1495,8 +1524,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
%{test_file: test_file}
|
%{test_file: test_file}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "strips / from filename", %{test_file: file} do
|
test "strips / from filename", %{test_file: %Plug.Upload{} = file} do
|
||||||
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
|
file = %{file | filename: "../../../../../nested/bad.jpg"}
|
||||||
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||||
[%{"href" => href}] = object.data["url"]
|
[%{"href" => href}] = object.data["url"]
|
||||||
assert Regex.match?(~r"/bad.jpg$", href)
|
assert Regex.match?(~r"/bad.jpg$", href)
|
||||||
|
|
@ -1754,13 +1783,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
test "fetch_activities/2 returns activities addressed to a list " do
|
test "fetch_activities/2 returns activities addressed to a list " do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
member = insert(:user)
|
member = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, member)
|
{:ok, list} = Pleroma.List.follow(list, member)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
{:ok, %Activity{} = activity} =
|
||||||
|
CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||||
|
|
||||||
activity = Repo.preload(activity, :bookmark)
|
activity = Repo.preload(activity, :bookmark)
|
||||||
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
|
activity = %{activity | thread_muted?: !!activity.thread_muted?}
|
||||||
|
|
||||||
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
|
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
|
||||||
end
|
end
|
||||||
|
|
@ -1960,7 +1990,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
assert User.following?(follower, old_user)
|
assert User.following?(follower, old_user)
|
||||||
assert User.following?(follower_move_opted_out, old_user)
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
|
|
||||||
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
|
assert {:ok, %Activity{} = activity} = ActivityPub.move(old_user, new_user)
|
||||||
|
|
||||||
assert %Activity{
|
assert %Activity{
|
||||||
actor: ^old_ap_id,
|
actor: ^old_ap_id,
|
||||||
|
|
@ -1992,7 +2022,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
assert User.following?(follower_move_opted_out, old_user)
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
refute User.following?(follower_move_opted_out, new_user)
|
refute User.following?(follower_move_opted_out, new_user)
|
||||||
|
|
||||||
activity = %Activity{activity | object: nil}
|
activity = %{activity | object: nil}
|
||||||
|
|
||||||
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
|
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,25 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
|
defp regexes_match!([],[]), do: true
|
||||||
|
|
||||||
|
defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do
|
||||||
|
authority.source == checked.source and regexes_match!(authority_rest, checked_rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp regexes_match!(_, _), do: false
|
||||||
|
|
||||||
test "subdomains_regex/1" do
|
test "subdomains_regex/1" do
|
||||||
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
|
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
|
||||||
~r/^unsafe.tld$/i,
|
|
||||||
~r/^(.*\.)*unsafe.tld$/i
|
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i])
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "subdomain_match/2" do
|
describe "subdomain_match/2" do
|
||||||
test "common domains" do
|
test "common domains" do
|
||||||
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
||||||
|
|
||||||
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]
|
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
||||||
|
|
@ -32,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "wildcard domains with one subdomain" do
|
test "wildcard domains with one subdomain" do
|
||||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||||
|
|
@ -43,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "wildcard domains with two subdomains" do
|
test "wildcard domains with two subdomains" do
|
||||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
||||||
|
|
@ -54,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "matches are case-insensitive" do
|
test "matches are case-insensitive" do
|
||||||
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
||||||
|
|
||||||
assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]
|
assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
||||||
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
||||||
|
|
|
||||||
|
|
@ -86,23 +86,32 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do
|
||||||
object = Object.normalize(post_activity, fetch: false)
|
object = Object.normalize(post_activity, fetch: false)
|
||||||
|
|
||||||
# Another user can't announce it
|
# Another user can't announce it
|
||||||
{:ok, announce, []} = Builder.announce(announcer, object, public: false)
|
{:ok, announce, []} = Builder.announce(announcer, object, visibility: "private")
|
||||||
|
|
||||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
assert {:actor, {"can not announce this object", []}} in cng.errors
|
assert {:actor, {"can not announce this object", []}} in cng.errors
|
||||||
|
|
||||||
# The actor of the object can announce it
|
# The actor of the object can announce it with a restrictive scope
|
||||||
{:ok, announce, []} = Builder.announce(user, object, public: false)
|
{:ok, announce, []} = Builder.announce(user, object, visibility: "private")
|
||||||
|
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
|
{:ok, announce, []} = Builder.announce(user, object, visibility: "direct")
|
||||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
# The actor of the object can not announce it publicly
|
# The actor of the object can not announce it publicly
|
||||||
{:ok, announce, []} = Builder.announce(user, object, public: true)
|
{:ok, announce, []} = Builder.announce(user, object, visibility: "public")
|
||||||
|
{:error, cng1} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
{:ok, announce, []} = Builder.announce(user, object, visibility: "unlisted")
|
||||||
|
{:error, cng2} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
{:ok, announce, []} = Builder.announce(user, object, visibility: "local")
|
||||||
|
{:error, cng3} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
|
for cng <- [cng1, cng2, cng3] do
|
||||||
|
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
||||||
|
|
||||||
test "activity with BCC is published to a list member." do
|
test "activity with BCC is published to a list member." do
|
||||||
actor = insert(:user)
|
actor = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("list", actor)
|
{:ok, list} = Pleroma.List.create(%{title: "list"}, actor)
|
||||||
list_member = insert(:user, %{local: false})
|
list_member = insert(:user, %{local: false})
|
||||||
|
|
||||||
Pleroma.List.follow(list, list_member)
|
Pleroma.List.follow(list, list_member)
|
||||||
|
|
|
||||||
|
|
@ -784,13 +784,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||||
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
||||||
|
|
||||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
|
{:ok, announce_data, _meta} = Builder.announce(user, post.object, visibility: "public")
|
||||||
|
|
||||||
{:ok, private_announce_data, _meta} =
|
{:ok, private_announce_data, _meta} =
|
||||||
Builder.announce(user, private_post.object, public: false)
|
Builder.announce(user, private_post.object, visibility: "private")
|
||||||
|
|
||||||
{:ok, relay_announce_data, _meta} =
|
{:ok, relay_announce_data, _meta} =
|
||||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
|
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object,
|
||||||
|
visibility: "public"
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
||||||
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
||||||
|
|
|
||||||
|
|
@ -581,7 +581,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
|
|
||||||
test "it strips BCC field" do
|
test "it strips BCC field" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do
|
||||||
following = insert(:user)
|
following = insert(:user)
|
||||||
unrelated = insert(:user)
|
unrelated = insert(:user)
|
||||||
{:ok, following, user} = Pleroma.User.follow(following, user)
|
{:ok, following, user} = Pleroma.User.follow(following, user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||||
|
|
||||||
Pleroma.List.follow(list, unrelated)
|
Pleroma.List.follow(list, unrelated)
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue