aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/user.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/user.ex')
-rw-r--r--lib/pleroma/user.ex172
1 files changed, 129 insertions, 43 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 3a9ae8d73..d03810d1a 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -9,12 +9,14 @@ defmodule Pleroma.User do
import Ecto.Query
alias Comeonin.Pbkdf2
+ alias Ecto.Multi
alias Pleroma.Activity
alias Pleroma.Keys
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Registration
alias Pleroma.Repo
+ alias Pleroma.RepoStreamer
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -105,15 +107,25 @@ defmodule Pleroma.User do
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
- def user_info(%User{} = user) do
+ def user_info(%User{} = user, args \\ %{}) do
+ following_count =
+ if args[:following_count], do: args[:following_count], else: following_count(user)
+
+ follower_count =
+ if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
+
%{
- following_count: following_count(user),
note_count: user.info.note_count,
- follower_count: user.info.follower_count,
locked: user.info.locked,
confirmation_pending: user.info.confirmation_pending,
default_scope: user.info.default_scope
}
+ |> Map.put(:following_count, following_count)
+ |> Map.put(:follower_count, follower_count)
+ end
+
+ def set_info_cache(user, args) do
+ Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
end
def restrict_deactivated(query) do
@@ -193,29 +205,26 @@ defmodule Pleroma.User do
end
def password_update_changeset(struct, params) do
- changeset =
- struct
- |> cast(params, [:password, :password_confirmation])
- |> validate_required([:password, :password_confirmation])
- |> validate_confirmation(:password)
-
- OAuth.Token.delete_user_tokens(struct)
- OAuth.Authorization.delete_user_authorizations(struct)
-
- if changeset.valid? do
- hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
-
- changeset
- |> put_change(:password_hash, hashed)
- else
- changeset
+ struct
+ |> cast(params, [:password, :password_confirmation])
+ |> validate_required([:password, :password_confirmation])
+ |> validate_confirmation(:password)
+ |> put_password_hash
+ end
+
+ def reset_password(%User{id: user_id} = user, data) do
+ multi =
+ Multi.new()
+ |> Multi.update(:user, password_update_changeset(user, data))
+ |> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
+ |> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
+
+ case Repo.transaction(multi) do
+ {:ok, %{user: user} = _} -> set_cache(user)
+ {:error, _, changeset, _} -> {:error, changeset}
end
end
- def reset_password(user, data) do
- update_and_set_cache(password_update_changeset(user, data))
- end
-
def register_changeset(struct, params \\ %{}, opts \\ []) do
need_confirmation? =
if is_nil(opts[:need_confirmation]) do
@@ -249,12 +258,11 @@ defmodule Pleroma.User do
end
if changeset.valid? do
- hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]})
followers = User.ap_followers(%User{nickname: changeset.changes[:nickname]})
changeset
- |> put_change(:password_hash, hashed)
+ |> put_password_hash
|> put_change(:ap_id, ap_id)
|> unique_constraint(:ap_id)
|> put_change(:following, [followers])
@@ -838,15 +846,12 @@ defmodule Pleroma.User do
def mutes?(nil, _), do: false
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
- def blocks?(user, %{ap_id: ap_id}) do
- blocks = user.info.blocks
- domain_blocks = user.info.domain_blocks
+ def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
+ blocks = info.blocks
+ domain_blocks = info.domain_blocks
%{host: host} = URI.parse(ap_id)
- Enum.member?(blocks, ap_id) ||
- Enum.any?(domain_blocks, fn domain ->
- host == domain
- end)
+ Enum.member?(blocks, ap_id) || Enum.any?(domain_blocks, &(&1 == host))
end
def subscribed_to?(user, %{ap_id: ap_id}) do
@@ -932,18 +937,24 @@ defmodule Pleroma.User do
@spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:delete, %User{} = user) do
- {:ok, user} = User.deactivate(user)
-
# Remove all relationships
{:ok, followers} = User.get_followers(user)
- Enum.each(followers, fn follower -> User.unfollow(follower, user) end)
+ Enum.each(followers, fn follower ->
+ ActivityPub.unfollow(follower, user)
+ User.unfollow(follower, user)
+ end)
{:ok, friends} = User.get_friends(user)
- Enum.each(friends, fn followed -> User.unfollow(user, followed) end)
+ Enum.each(friends, fn followed ->
+ ActivityPub.unfollow(user, followed)
+ User.unfollow(user, followed)
+ end)
delete_user_activities(user)
+
+ {:ok, _user} = Repo.delete(user)
end
@spec perform(atom(), User.t()) :: {:ok, User.t()}
@@ -999,6 +1010,56 @@ defmodule Pleroma.User do
)
end
+ @spec sync_follow_counter() :: :ok
+ def sync_follow_counter,
+ do: PleromaJobQueue.enqueue(:background, __MODULE__, [:sync_follow_counters])
+
+ @spec perform(:sync_follow_counters) :: :ok
+ def perform(:sync_follow_counters) do
+ {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
+ config = Pleroma.Config.get([:instance, :external_user_synchronization])
+
+ :ok = sync_follow_counters(config)
+ Agent.stop(:domain_errors)
+ end
+
+ @spec sync_follow_counters(keyword()) :: :ok
+ def sync_follow_counters(opts \\ []) do
+ users = external_users(opts)
+
+ if length(users) > 0 do
+ errors = Agent.get(:domain_errors, fn state -> state end)
+ {last, updated_errors} = User.Synchronization.call(users, errors, opts)
+ Agent.update(:domain_errors, fn _state -> updated_errors end)
+ sync_follow_counters(max_id: last.id, limit: opts[:limit])
+ else
+ :ok
+ end
+ end
+
+ @spec external_users(keyword()) :: [User.t()]
+ def external_users(opts \\ []) do
+ query =
+ User.Query.build(%{
+ external: true,
+ active: true,
+ order_by: :id,
+ select: [:id, :ap_id, :info]
+ })
+
+ query =
+ if opts[:max_id],
+ do: where(query, [u], u.id > ^opts[:max_id]),
+ else: query
+
+ query =
+ if opts[:limit],
+ do: limit(query, ^opts[:limit]),
+ else: query
+
+ Repo.all(query)
+ end
+
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers),
do:
PleromaJobQueue.enqueue(:background, __MODULE__, [
@@ -1016,18 +1077,35 @@ defmodule Pleroma.User do
])
def delete_user_activities(%User{ap_id: ap_id} = user) do
- stream =
- ap_id
- |> Activity.query_by_actor()
- |> Repo.stream()
-
- Repo.transaction(fn -> Enum.each(stream, &delete_activity(&1)) end, timeout: :infinity)
+ ap_id
+ |> Activity.query_by_actor()
+ |> RepoStreamer.chunk_stream(50)
+ |> Stream.each(fn activities ->
+ Enum.each(activities, &delete_activity(&1))
+ end)
+ |> Stream.run()
{:ok, user}
end
defp delete_activity(%{data: %{"type" => "Create"}} = activity) do
- Object.normalize(activity) |> ActivityPub.delete()
+ activity
+ |> Object.normalize()
+ |> ActivityPub.delete()
+ end
+
+ defp delete_activity(%{data: %{"type" => "Like"}} = activity) do
+ user = get_cached_by_ap_id(activity.actor)
+ object = Object.normalize(activity)
+
+ ActivityPub.unlike(user, object)
+ end
+
+ defp delete_activity(%{data: %{"type" => "Announce"}} = activity) do
+ user = get_cached_by_ap_id(activity.actor)
+ object = Object.normalize(activity)
+
+ ActivityPub.unannounce(user, object)
end
defp delete_activity(_activity), do: "Doing nothing"
@@ -1325,4 +1403,12 @@ defmodule Pleroma.User do
end
defdelegate search(query, opts \\ []), to: User.Search
+
+ defp put_password_hash(
+ %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
+ ) do
+ change(changeset, password_hash: Pbkdf2.hashpwsalt(password))
+ end
+
+ defp put_password_hash(changeset), do: changeset
end