Resolve conflicts
This commit is contained in:
commit
01fe5abad1
30 changed files with 1016 additions and 380 deletions
|
|
@ -7,7 +7,7 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
|
|||
|
||||
Benchee.run(%{
|
||||
"search" => fn ->
|
||||
Pleroma.Web.MastodonAPI.MastodonAPIController.status_search(nil, "cofe")
|
||||
Pleroma.Activity.search(nil, "cofe")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -343,4 +343,6 @@ defmodule Pleroma.Activity do
|
|||
)
|
||||
)
|
||||
end
|
||||
|
||||
defdelegate search(user, query), to: Pleroma.Activity.Search
|
||||
end
|
||||
|
|
|
|||
81
lib/pleroma/activity/search.ex
Normal file
81
lib/pleroma/activity/search.ex
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.Search do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
def search(user, search_query) do
|
||||
index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin
|
||||
|
||||
Activity
|
||||
|> Activity.with_preloaded_object()
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> restrict_public()
|
||||
|> query_with(index_type, search_query)
|
||||
|> maybe_restrict_local(user)
|
||||
|> Repo.all()
|
||||
|> maybe_fetch(user, search_query)
|
||||
end
|
||||
|
||||
defp restrict_public(q) do
|
||||
from([a, o] in q,
|
||||
where: fragment("?->>'type' = 'Create'", a.data),
|
||||
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
|
||||
limit: 40
|
||||
)
|
||||
end
|
||||
|
||||
defp query_with(q, :gin, search_query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
|
||||
o.data,
|
||||
^search_query
|
||||
),
|
||||
order_by: [desc: :id]
|
||||
)
|
||||
end
|
||||
|
||||
defp query_with(q, :rum, search_query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"? @@ plainto_tsquery('english', ?)",
|
||||
o.fts_content,
|
||||
^search_query
|
||||
),
|
||||
order_by: [fragment("? <=> now()::date", o.inserted_at)]
|
||||
)
|
||||
end
|
||||
|
||||
# users can search everything
|
||||
defp maybe_restrict_local(q, %User{}), do: q
|
||||
|
||||
# unauthenticated users can only search local activities
|
||||
defp maybe_restrict_local(q, _) do
|
||||
if Pleroma.Config.get([:instance, :limit_unauthenticated_to_local_content], true) do
|
||||
where(q, local: true)
|
||||
else
|
||||
q
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_fetch(activities, user, search_query) do
|
||||
with true <- Regex.match?(~r/https?:/, search_query),
|
||||
{:ok, object} <- Fetcher.fetch_object_from_id(search_query),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
activities ++ [activity]
|
||||
else
|
||||
_ -> activities
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
defmodule Pleroma.Application do
|
||||
use Application
|
||||
import Supervisor.Spec
|
||||
|
||||
@name Mix.Project.config()[:name]
|
||||
@version Mix.Project.config()[:version]
|
||||
|
|
@ -31,97 +30,131 @@ defmodule Pleroma.Application do
|
|||
children =
|
||||
[
|
||||
# Start the Ecto repository
|
||||
supervisor(Pleroma.Repo, []),
|
||||
worker(Pleroma.Emoji, []),
|
||||
worker(Pleroma.Captcha, []),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:used_captcha_cache,
|
||||
[
|
||||
ttl_interval: :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||
]
|
||||
],
|
||||
id: :cachex_used_captcha_cache
|
||||
),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:user_cache,
|
||||
[
|
||||
default_ttl: 25_000,
|
||||
ttl_interval: 1000,
|
||||
limit: 2500
|
||||
]
|
||||
],
|
||||
id: :cachex_user
|
||||
),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:object_cache,
|
||||
[
|
||||
default_ttl: 25_000,
|
||||
ttl_interval: 1000,
|
||||
limit: 2500
|
||||
]
|
||||
],
|
||||
id: :cachex_object
|
||||
),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:rich_media_cache,
|
||||
[
|
||||
default_ttl: :timer.minutes(120),
|
||||
limit: 5000
|
||||
]
|
||||
],
|
||||
id: :cachex_rich_media
|
||||
),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:scrubber_cache,
|
||||
[
|
||||
limit: 2500
|
||||
]
|
||||
],
|
||||
id: :cachex_scrubber
|
||||
),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
:idempotency_cache,
|
||||
[
|
||||
expiration:
|
||||
expiration(
|
||||
default: :timer.seconds(6 * 60 * 60),
|
||||
interval: :timer.seconds(60)
|
||||
),
|
||||
limit: 2500
|
||||
]
|
||||
],
|
||||
id: :cachex_idem
|
||||
),
|
||||
worker(Pleroma.FlakeId, []),
|
||||
worker(Pleroma.ScheduledActivityWorker, []),
|
||||
worker(Pleroma.QuantumScheduler, [])
|
||||
%{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor},
|
||||
%{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}},
|
||||
%{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}},
|
||||
%{
|
||||
id: :cachex_used_captcha_cache,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:used_captcha_cache,
|
||||
[
|
||||
ttl_interval:
|
||||
:timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{
|
||||
id: :cachex_user,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:user_cache,
|
||||
[
|
||||
default_ttl: 25_000,
|
||||
ttl_interval: 1000,
|
||||
limit: 2500
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{
|
||||
id: :cachex_object,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:object_cache,
|
||||
[
|
||||
default_ttl: 25_000,
|
||||
ttl_interval: 1000,
|
||||
limit: 2500
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{
|
||||
id: :cachex_rich_media,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:rich_media_cache,
|
||||
[
|
||||
default_ttl: :timer.minutes(120),
|
||||
limit: 5000
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{
|
||||
id: :cachex_scrubber,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:scrubber_cache,
|
||||
[
|
||||
limit: 2500
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{
|
||||
id: :cachex_idem,
|
||||
start:
|
||||
{Cachex, :start_link,
|
||||
[
|
||||
:idempotency_cache,
|
||||
[
|
||||
expiration:
|
||||
expiration(
|
||||
default: :timer.seconds(6 * 60 * 60),
|
||||
interval: :timer.seconds(60)
|
||||
),
|
||||
limit: 2500
|
||||
]
|
||||
]}
|
||||
},
|
||||
%{id: Pleroma.FlakeId, start: {Pleroma.FlakeId, :start_link, []}},
|
||||
%{
|
||||
id: Pleroma.ScheduledActivityWorker,
|
||||
start: {Pleroma.ScheduledActivityWorker, :start_link, []}
|
||||
},
|
||||
%{
|
||||
id: Pleroma.QuantumScheduler,
|
||||
start: {Pleroma.QuantumScheduler, :start_link, []}
|
||||
}
|
||||
] ++
|
||||
hackney_pool_children() ++
|
||||
[
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
|
||||
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
|
||||
%{
|
||||
id: Pleroma.Web.Federator.RetryQueue,
|
||||
start: {Pleroma.Web.Federator.RetryQueue, :start_link, []}
|
||||
},
|
||||
%{
|
||||
id: Pleroma.Web.OAuth.Token.CleanWorker,
|
||||
start: {Pleroma.Web.OAuth.Token.CleanWorker, :start_link, []}
|
||||
},
|
||||
%{
|
||||
id: Pleroma.Stats,
|
||||
start: {Pleroma.Stats, :start_link, []}
|
||||
},
|
||||
%{
|
||||
id: :web_push_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
|
||||
restart: :temporary
|
||||
},
|
||||
%{
|
||||
id: :federator_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.Federator.init/0]},
|
||||
restart: :temporary
|
||||
}
|
||||
] ++
|
||||
streamer_child() ++
|
||||
chat_child() ++
|
||||
[
|
||||
# Start the endpoint when the application starts
|
||||
supervisor(Pleroma.Web.Endpoint, []),
|
||||
worker(Pleroma.Gopher.Server, [])
|
||||
%{
|
||||
id: Pleroma.Web.Endpoint,
|
||||
start: {Pleroma.Web.Endpoint, :start_link, []},
|
||||
type: :supervisor
|
||||
},
|
||||
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
|
||||
]
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||
|
|
@ -172,12 +205,17 @@ defmodule Pleroma.Application do
|
|||
defp chat_child, do: []
|
||||
else
|
||||
defp streamer_child do
|
||||
[worker(Pleroma.Web.Streamer, [])]
|
||||
[%{id: Pleroma.Web.Streamer, start: {Pleroma.Web.Streamer, :start_link, []}}]
|
||||
end
|
||||
|
||||
defp chat_child do
|
||||
if Pleroma.Config.get([:chat, :enabled]) do
|
||||
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
||||
[
|
||||
%{
|
||||
id: Pleroma.Web.ChatChannel.ChatChannelState,
|
||||
start: {Pleroma.Web.ChatChannel.ChatChannelState, :start_link, []}
|
||||
}
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -325,14 +325,6 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def maybe_follow(%User{} = follower, %User{info: _info} = followed) do
|
||||
if not following?(follower, followed) do
|
||||
follow(follower, followed)
|
||||
else
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
||||
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
||||
def follow_all(follower, followeds) do
|
||||
|
|
@ -371,8 +363,8 @@ defmodule Pleroma.User do
|
|||
ap_followers = followed.follower_address
|
||||
|
||||
cond do
|
||||
following?(follower, followed) or info.deactivated ->
|
||||
{:error, "Could not follow user: #{followed.nickname} is already on your list."}
|
||||
info.deactivated ->
|
||||
{:error, "Could not follow user: You are deactivated."}
|
||||
|
||||
deny_follow_blocked and blocks?(followed, follower) ->
|
||||
{:error, "Could not follow user: #{followed.nickname} blocked you."}
|
||||
|
|
@ -736,122 +728,6 @@ defmodule Pleroma.User do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
def search(query, resolve \\ false, for_user \\ nil) do
|
||||
# Strip the beginning @ off if there is a query
|
||||
query = String.trim_leading(query, "@")
|
||||
|
||||
if resolve, do: get_or_fetch(query)
|
||||
|
||||
{:ok, results} =
|
||||
Repo.transaction(fn ->
|
||||
Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
|
||||
Repo.all(search_query(query, for_user))
|
||||
end)
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def search_query(query, for_user) do
|
||||
fts_subquery = fts_search_subquery(query)
|
||||
trigram_subquery = trigram_search_subquery(query)
|
||||
union_query = from(s in trigram_subquery, union_all: ^fts_subquery)
|
||||
distinct_query = from(s in subquery(union_query), order_by: s.search_type, distinct: s.id)
|
||||
|
||||
from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
|
||||
order_by: [desc: s.search_rank],
|
||||
limit: 40
|
||||
)
|
||||
end
|
||||
|
||||
defp boost_search_rank_query(query, nil), do: query
|
||||
|
||||
defp boost_search_rank_query(query, for_user) do
|
||||
friends_ids = get_friends_ids(for_user)
|
||||
followers_ids = get_followers_ids(for_user)
|
||||
|
||||
from(u in subquery(query),
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
CASE WHEN (?) THEN (?) * 1.3
|
||||
WHEN (?) THEN (?) * 1.2
|
||||
WHEN (?) THEN (?) * 1.1
|
||||
ELSE (?) END
|
||||
""",
|
||||
u.id in ^friends_ids and u.id in ^followers_ids,
|
||||
u.search_rank,
|
||||
u.id in ^friends_ids,
|
||||
u.search_rank,
|
||||
u.id in ^followers_ids,
|
||||
u.search_rank,
|
||||
u.search_rank
|
||||
)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp fts_search_subquery(term, query \\ User) do
|
||||
processed_query =
|
||||
term
|
||||
|> String.replace(~r/\W+/, " ")
|
||||
|> String.trim()
|
||||
|> String.split()
|
||||
|> Enum.map(&(&1 <> ":*"))
|
||||
|> Enum.join(" | ")
|
||||
|
||||
from(
|
||||
u in query,
|
||||
select_merge: %{
|
||||
search_type: ^0,
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
ts_rank_cd(
|
||||
setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
|
||||
to_tsquery('simple', ?),
|
||||
32
|
||||
)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
},
|
||||
where:
|
||||
fragment(
|
||||
"""
|
||||
(setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
)
|
||||
|> restrict_deactivated()
|
||||
end
|
||||
|
||||
defp trigram_search_subquery(term) do
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
# ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
|
||||
search_type: fragment("?", 1),
|
||||
search_rank:
|
||||
fragment(
|
||||
"similarity(?, trim(? || ' ' || coalesce(?, '')))",
|
||||
^term,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
},
|
||||
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
|
||||
)
|
||||
|> restrict_deactivated()
|
||||
end
|
||||
|
||||
def mute(muter, %User{ap_id: ap_id}) do
|
||||
info_cng =
|
||||
muter.info
|
||||
|
|
@ -1516,4 +1392,14 @@ defmodule Pleroma.User do
|
|||
update_and_set_cache(cng)
|
||||
end
|
||||
end
|
||||
|
||||
def get_ap_ids_by_nicknames(nicknames) do
|
||||
from(u in User,
|
||||
where: u.nickname in ^nicknames,
|
||||
select: u.ap_id
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
defdelegate search(query, opts \\ []), to: User.Search
|
||||
end
|
||||
|
|
|
|||
166
lib/pleroma/user/search.ex
Normal file
166
lib/pleroma/user/search.ex
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.Search do
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
import Ecto.Query
|
||||
|
||||
def search(query, opts \\ []) do
|
||||
resolve = Keyword.get(opts, :resolve, false)
|
||||
for_user = Keyword.get(opts, :for_user)
|
||||
|
||||
# Strip the beginning @ off if there is a query
|
||||
query = String.trim_leading(query, "@")
|
||||
|
||||
maybe_resolve(resolve, for_user, query)
|
||||
|
||||
{:ok, results} =
|
||||
Repo.transaction(fn ->
|
||||
Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
|
||||
|
||||
query
|
||||
|> search_query(for_user)
|
||||
|> Repo.all()
|
||||
end)
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
defp maybe_resolve(true, %User{}, query) do
|
||||
User.get_or_fetch(query)
|
||||
end
|
||||
|
||||
defp maybe_resolve(true, _, query) do
|
||||
unless restrict_local?(), do: User.get_or_fetch(query)
|
||||
end
|
||||
|
||||
defp maybe_resolve(_, _, _), do: :noop
|
||||
|
||||
defp search_query(query, for_user) do
|
||||
query
|
||||
|> union_query()
|
||||
|> distinct_query()
|
||||
|> boost_search_rank_query(for_user)
|
||||
|> subquery()
|
||||
|> order_by(desc: :search_rank)
|
||||
|> limit(20)
|
||||
|> maybe_restrict_local(for_user)
|
||||
end
|
||||
|
||||
defp restrict_local? do
|
||||
Pleroma.Config.get([:instance, :limit_unauthenticated_to_local_content], true)
|
||||
end
|
||||
|
||||
defp union_query(query) do
|
||||
fts_subquery = fts_search_subquery(query)
|
||||
trigram_subquery = trigram_search_subquery(query)
|
||||
|
||||
from(s in trigram_subquery, union_all: ^fts_subquery)
|
||||
end
|
||||
|
||||
defp distinct_query(q) do
|
||||
from(s in subquery(q), order_by: s.search_type, distinct: s.id)
|
||||
end
|
||||
|
||||
# unauthenticated users can only search local activities
|
||||
defp maybe_restrict_local(q, %User{}), do: q
|
||||
|
||||
defp maybe_restrict_local(q, _) do
|
||||
if restrict_local?() do
|
||||
where(q, [u], u.local == true)
|
||||
else
|
||||
q
|
||||
end
|
||||
end
|
||||
|
||||
defp boost_search_rank_query(query, nil), do: query
|
||||
|
||||
defp boost_search_rank_query(query, for_user) do
|
||||
friends_ids = User.get_friends_ids(for_user)
|
||||
followers_ids = User.get_followers_ids(for_user)
|
||||
|
||||
from(u in subquery(query),
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
CASE WHEN (?) THEN 0.5 + (?) * 1.3
|
||||
WHEN (?) THEN 0.5 + (?) * 1.2
|
||||
WHEN (?) THEN (?) * 1.1
|
||||
ELSE (?) END
|
||||
""",
|
||||
u.id in ^friends_ids and u.id in ^followers_ids,
|
||||
u.search_rank,
|
||||
u.id in ^friends_ids,
|
||||
u.search_rank,
|
||||
u.id in ^followers_ids,
|
||||
u.search_rank,
|
||||
u.search_rank
|
||||
)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp fts_search_subquery(term, query \\ User) do
|
||||
processed_query =
|
||||
term
|
||||
|> String.replace(~r/\W+/, " ")
|
||||
|> String.trim()
|
||||
|> String.split()
|
||||
|> Enum.map(&(&1 <> ":*"))
|
||||
|> Enum.join(" | ")
|
||||
|
||||
from(
|
||||
u in query,
|
||||
select_merge: %{
|
||||
search_type: ^0,
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
ts_rank_cd(
|
||||
setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
|
||||
to_tsquery('simple', ?),
|
||||
32
|
||||
)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
},
|
||||
where:
|
||||
fragment(
|
||||
"""
|
||||
(setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
)
|
||||
|> User.restrict_deactivated()
|
||||
end
|
||||
|
||||
defp trigram_search_subquery(term) do
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
# ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
|
||||
search_type: fragment("?", 1),
|
||||
search_rank:
|
||||
fragment(
|
||||
"similarity(?, trim(? || ' ' || coalesce(?, '')))",
|
||||
^term,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
},
|
||||
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
|
||||
)
|
||||
|> User.restrict_deactivated()
|
||||
end
|
||||
end
|
||||
|
|
@ -458,10 +458,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
|
||||
{:user_blocked, false} <-
|
||||
{_, false} <-
|
||||
{:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
|
||||
{:user_locked, false} <- {:user_locked, User.locked?(followed)},
|
||||
{:follow, {:ok, follower}} <- {:follow, User.follow(follower, followed)} do
|
||||
{_, false} <- {:user_locked, User.locked?(followed)},
|
||||
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
|
||||
{_, {:ok, _}} <-
|
||||
{:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do
|
||||
ActivityPub.accept(%{
|
||||
to: [follower.ap_id],
|
||||
actor: followed,
|
||||
|
|
@ -470,7 +472,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
})
|
||||
else
|
||||
{:user_blocked, true} ->
|
||||
{:ok, _} = Utils.update_follow_state(activity, "reject")
|
||||
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
|
||||
|
||||
ActivityPub.reject(%{
|
||||
to: [follower.ap_id],
|
||||
|
|
@ -480,7 +482,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
})
|
||||
|
||||
{:follow, {:error, _}} ->
|
||||
{:ok, _} = Utils.update_follow_state(activity, "reject")
|
||||
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
|
||||
|
||||
ActivityPub.reject(%{
|
||||
to: [follower.ap_id],
|
||||
|
|
@ -506,21 +508,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with actor <- Containment.get_actor(data),
|
||||
{:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
||||
{:ok, activity} <-
|
||||
ActivityPub.accept(%{
|
||||
to: follow_activity.data["to"],
|
||||
type: "Accept",
|
||||
actor: followed,
|
||||
object: follow_activity.data["id"],
|
||||
local: false
|
||||
}) do
|
||||
if not User.following?(follower, followed) do
|
||||
{:ok, _follower} = User.follow(follower, followed)
|
||||
end
|
||||
|
||||
{:ok, activity}
|
||||
{:ok, _follower} = User.follow(follower, followed) do
|
||||
ActivityPub.accept(%{
|
||||
to: follow_activity.data["to"],
|
||||
type: "Accept",
|
||||
actor: followed,
|
||||
object: follow_activity.data["id"],
|
||||
local: false
|
||||
})
|
||||
else
|
||||
_e -> :error
|
||||
end
|
||||
|
|
@ -532,7 +529,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with actor <- Containment.get_actor(data),
|
||||
{:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
|
||||
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
||||
{:ok, activity} <-
|
||||
ActivityPub.reject(%{
|
||||
|
|
|
|||
|
|
@ -376,8 +376,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Updates a follow activity's state (for locked accounts).
|
||||
"""
|
||||
def update_follow_state(
|
||||
%Activity{data: %{"actor" => actor, "object" => object, "state" => "pending"}} = activity,
|
||||
def update_follow_state_for_all(
|
||||
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
|
||||
state
|
||||
) do
|
||||
try do
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets or creates Pleroma.Registration record from Ueberauth assigns.
|
||||
Note: some strategies (like `keycloak`) might need extra configuration to fill `uid` from callback response —
|
||||
see [`docs/config.md`](docs/config.md).
|
||||
"""
|
||||
def get_registration(%Plug.Conn{assigns: %{ueberauth_auth: %{uid: nil}}}),
|
||||
do: {:error, :missing_uid}
|
||||
|
||||
def get_registration(%Plug.Conn{
|
||||
assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}
|
||||
}) do
|
||||
|
|
@ -51,9 +59,10 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
|
||||
def get_registration(%Plug.Conn{} = _conn), do: {:error, :missing_credentials}
|
||||
|
||||
@doc "Creates Pleroma.User record basing on params and Pleroma.Registration record."
|
||||
def create_from_registration(
|
||||
%Plug.Conn{params: %{"authorization" => registration_attrs}},
|
||||
registration
|
||||
%Registration{} = registration
|
||||
) do
|
||||
nickname = value([registration_attrs["nickname"], Registration.nickname(registration)])
|
||||
email = value([registration_attrs["email"], Registration.email(registration)])
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
|
||||
def accept_follow_request(follower, followed) do
|
||||
with {:ok, follower} <- User.maybe_follow(follower, followed),
|
||||
with {:ok, follower} <- User.follow(follower, followed),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.accept(%{
|
||||
to: [follower.ap_id],
|
||||
|
|
@ -51,7 +51,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
def reject_follow_request(follower, followed) do
|
||||
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
|
||||
{:ok, _activity} <-
|
||||
ActivityPub.reject(%{
|
||||
to: [follower.ap_id],
|
||||
|
|
@ -204,8 +204,10 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
data,
|
||||
visibility
|
||||
),
|
||||
mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.ap_id),
|
||||
addressed_users <- get_addressed_users(mentioned_users, data["to"]),
|
||||
{poll, poll_emoji} <- make_poll_data(data),
|
||||
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
||||
{to, cc} <- get_to_and_cc(user, addressed_users, in_reply_to, visibility),
|
||||
context <- make_context(in_reply_to),
|
||||
cw <- data["spoiler_text"] || "",
|
||||
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end)
|
||||
end
|
||||
|
||||
def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do
|
||||
mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
|
||||
|
||||
@spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
|
||||
{list(String.t()), list(String.t())}
|
||||
def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
|
||||
to = ["https://www.w3.org/ns/activitystreams#Public" | mentioned_users]
|
||||
cc = [user.follower_address]
|
||||
|
||||
|
|
@ -74,9 +74,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def to_for_user_and_mentions(user, mentions, inReplyTo, "unlisted") do
|
||||
mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
|
||||
|
||||
def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do
|
||||
to = [user.follower_address | mentioned_users]
|
||||
cc = ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
|
||||
|
|
@ -87,14 +85,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def to_for_user_and_mentions(user, mentions, inReplyTo, "private") do
|
||||
{to, cc} = to_for_user_and_mentions(user, mentions, inReplyTo, "direct")
|
||||
def get_to_and_cc(user, mentioned_users, inReplyTo, "private") do
|
||||
{to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct")
|
||||
{[user.follower_address | to], cc}
|
||||
end
|
||||
|
||||
def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do
|
||||
mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
|
||||
|
||||
def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct") do
|
||||
if inReplyTo do
|
||||
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
|
||||
else
|
||||
|
|
@ -102,6 +98,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def get_addressed_users(_, to) when is_list(to) do
|
||||
User.get_ap_ids_by_nicknames(to)
|
||||
end
|
||||
|
||||
def get_addressed_users(mentioned_users, _), do: mentioned_users
|
||||
|
||||
def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)
|
||||
when is_list(options) do
|
||||
%{max_expiration: max_expiration, min_expiration: min_expiration} =
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.HTTP
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ScheduledActivity
|
||||
|
|
@ -1125,64 +1124,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def status_search_query_with_gin(q, query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
|
||||
o.data,
|
||||
^query
|
||||
),
|
||||
order_by: [desc: :id]
|
||||
)
|
||||
end
|
||||
|
||||
def status_search_query_with_rum(q, query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"? @@ plainto_tsquery('english', ?)",
|
||||
o.fts_content,
|
||||
^query
|
||||
),
|
||||
order_by: [fragment("? <=> now()::date", o.inserted_at)]
|
||||
)
|
||||
end
|
||||
|
||||
def status_search(user, query) do
|
||||
fetched =
|
||||
if Regex.match?(~r/https?:/, query) do
|
||||
with {:ok, object} <- Fetcher.fetch_object_from_id(query),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
[activity]
|
||||
else
|
||||
_e -> []
|
||||
end
|
||||
end || []
|
||||
|
||||
q =
|
||||
from([a, o] in Activity.with_preloaded_object(Activity),
|
||||
where: fragment("?->>'type' = 'Create'", a.data),
|
||||
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
|
||||
limit: 40
|
||||
)
|
||||
|
||||
q =
|
||||
if Pleroma.Config.get([:database, :rum_enabled]) do
|
||||
status_search_query_with_rum(q, query)
|
||||
else
|
||||
status_search_query_with_gin(q, query)
|
||||
end
|
||||
|
||||
Repo.all(q) ++ fetched
|
||||
end
|
||||
|
||||
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
|
||||
statuses = Activity.search(user, query)
|
||||
tags_path = Web.base_url() <> "/tag/"
|
||||
|
||||
tags =
|
||||
|
|
@ -1205,8 +1149,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
statuses = Activity.search(user, query)
|
||||
|
||||
tags =
|
||||
query
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
"mastodon_api",
|
||||
"mastodon_api_streaming",
|
||||
"polls",
|
||||
"pleroma_explicit_addressing",
|
||||
if Config.get([:media_proxy, :enabled]) do
|
||||
"media_proxy"
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
|
||||
require Logger
|
||||
|
||||
if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth)
|
||||
|
||||
plug(:fetch_session)
|
||||
|
|
@ -318,7 +320,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
|> registration_details(%{"authorization" => registration_params})
|
||||
end
|
||||
else
|
||||
_ ->
|
||||
error ->
|
||||
Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
|
||||
|
||||
conn
|
||||
|> put_flash(:error, "Failed to set up user account.")
|
||||
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue