From db690bede92e9ba65b1afaa8605aa5ddb3c992a4 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 2 Aug 2019 21:33:12 +0300 Subject: temp commit --- lib/load_testing/fetcher.ex | 116 ++++++++++++++++++++++++++++++++++ lib/load_testing/generator.ex | 83 ++++++++++++++++++++++++ lib/load_testing/helper.ex | 16 +++++ lib/mix/tasks/pleroma/load_testing.ex | 100 +++++++++++++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 lib/load_testing/fetcher.ex create mode 100644 lib/load_testing/generator.ex create mode 100644 lib/load_testing/helper.ex create mode 100644 lib/mix/tasks/pleroma/load_testing.ex (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex new file mode 100644 index 000000000..70c0fcd0c --- /dev/null +++ b/lib/load_testing/fetcher.ex @@ -0,0 +1,116 @@ +defmodule Pleroma.LoadTesting.Fetcher do + use Pleroma.LoadTesting.Helper + + def fetch_user(user) do + IO.puts("=================================") + + {time, _value} = :timer.tc(fn -> Repo.get_by(User, id: user.id) end) + + IO.puts("Query user by id: #{to_sec(time)} sec.") + + {time, _value} = + :timer.tc(fn -> + Repo.get_by(User, ap_id: user.ap_id) + end) + + IO.puts("Query user by ap_id: #{to_sec(time)} sec.") + + {time, _value} = + :timer.tc(fn -> + Repo.get_by(User, email: user.email) + end) + + IO.puts("Query user by email: #{to_sec(time)} sec.") + + {time, _value} = :timer.tc(fn -> Repo.get_by(User, nickname: user.nickname) end) + + IO.puts("Query user by nickname: #{to_sec(time)} sec.") + end + + def query_timelines(user) do + IO.puts("\n=================================") + + params = %{ + "count" => 20, + "with_muted" => true, + "type" => ["Create", "Announce"], + "blocking_user" => user, + "muting_user" => user, + "user" => user + } + + {time, _} = + :timer.tc(fn -> + ActivityPub.ActivityPub.fetch_activities([user.ap_id | user.following], params) + end) + + IO.puts("Query user home timeline: #{to_sec(time)} sec.") + + params = %{ + "count" => 20, + "local_only" => true, + "only_media" => "false", + "type" => ["Create", "Announce"], + "with_muted" => "true", + "blocking_user" => user, + "muting_user" => user + } + + {time, _} = + :timer.tc(fn -> + ActivityPub.ActivityPub.fetch_public_activities(params) + end) + + IO.puts("Query user mastodon public timeline: #{to_sec(time)} sec.") + + params = %{ + "count" => 20, + "only_media" => "false", + "type" => ["Create", "Announce"], + "with_muted" => "true", + "blocking_user" => user, + "muting_user" => user + } + + {time, _} = + :timer.tc(fn -> + ActivityPub.ActivityPub.fetch_public_activities(params) + end) + + IO.puts("Query user mastodon federated public timeline: #{to_sec(time)} sec.") + end + + def query_notifications(user) do + IO.puts("\n=================================") + params = %{"count" => "20", "with_muted" => "false"} + + {time, _} = + :timer.tc(fn -> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, params) end) + + IO.puts("Query user notifications with out muted: #{to_sec(time)} sec.") + + params = %{"count" => "20", "with_muted" => "true"} + + {time, _} = + :timer.tc(fn -> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, params) end) + + IO.puts("Query user notifications with muted: #{to_sec(time)} sec.") + end + + def query_long_thread(user, activity) do + IO.puts("\n=================================") + + {time, replies} = + :timer.tc(fn -> + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( + activity.data["context"], + %{ + "blocking_user" => user, + "user" => user + } + ) + end) + + IO.puts("Query long thread with #{length(replies)} replies: #{to_sec(time)} sec.") + end +end diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex new file mode 100644 index 000000000..a9016b9e8 --- /dev/null +++ b/lib/load_testing/generator.ex @@ -0,0 +1,83 @@ +defmodule Pleroma.LoadTesting.Generator do + use Pleroma.LoadTesting.Helper + + def generate_users(opts) do + IO.puts("Starting generating #{opts[:users_max]} users...") + {time, _} = :timer.tc(fn -> do_generate_users(opts) end) + IO.puts("Inserting users take #{to_sec(time)} sec.\n") + end + + defp do_generate_users(opts) do + min = Keyword.get(opts, :users_min, 1) + max = Keyword.get(opts, :users_max) + + query = + "INSERT INTO \"users\" (\"ap_id\",\"bio\",\"email\",\"follower_address\",\"following\",\"following_address\",\"info\", + \"local\",\"name\",\"nickname\",\"password_hash\",\"tags\",\"id\",\"inserted_at\",\"updated_at\") VALUES \n" + + users = + Task.async_stream( + min..max, + &generate_user_data(&1), + max_concurrency: 10, + timeout: 30_000 + ) + |> Enum.reduce("", fn {:ok, data}, acc -> acc <> data <> ", \n" end) + + query = query <> String.replace_trailing(users, ", \n", ";") + + Ecto.Adapters.SQL.query!(Repo, query) + end + + defp generate_user_data(i) do + user = %User{ + name: "Test テスト User #{i}", + email: "user#{i}@example.com", + nickname: "nick#{i}", + password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), + bio: "Tester Number #{i}", + info: %{} + } + + user = %{ + user + | ap_id: User.ap_id(user), + follower_address: User.ap_followers(user), + following_address: User.ap_following(user), + following: [User.ap_id(user)] + } + + "('#{user.ap_id}', '#{user.bio}', '#{user.email}', '#{user.follower_address}', '{#{ + user.following + }}', '#{user.following_address}', '#{Jason.encode!(user.info)}', '#{user.local}', '#{ + user.name + }', '#{user.nickname}', '#{user.password_hash}', '{#{user.tags}}', uuid_generate_v4(), NOW(), NOW())" + end + + def generate_activities(users, opts) do + IO.puts("Starting generating #{opts[:activities_max]} activities...") + {time, _} = :timer.tc(fn -> do_generate_activities(users, opts) end) + IO.puts("Inserting activities take #{to_sec(time)} sec.\n") + end + + defp do_generate_activities(users, opts) do + Task.async_stream( + 1..opts[:activities_max], + fn _ -> + do_generate_activity(users, opts) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end + + defp do_generate_activity(users, opts) do + status = + if opts[:mention], + do: "some status with @#{opts[:mention].nickname}", + else: "some status" + + Pleroma.Web.CommonAPI.post(Enum.random(users), %{"status" => status}) + end +end diff --git a/lib/load_testing/helper.ex b/lib/load_testing/helper.ex new file mode 100644 index 000000000..338dba323 --- /dev/null +++ b/lib/load_testing/helper.ex @@ -0,0 +1,16 @@ +defmodule Pleroma.LoadTesting.Helper do + defmacro __using__(_) do + quote do + import Ecto.Query + alias Pleroma.Activity + alias Pleroma.Notification + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub + alias Pleroma.Web.CommonAPI + + defp to_sec(microseconds), do: microseconds / 1_000_000 + end + end +end diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex new file mode 100644 index 000000000..9ed30db7e --- /dev/null +++ b/lib/mix/tasks/pleroma/load_testing.ex @@ -0,0 +1,100 @@ +defmodule Mix.Tasks.Pleroma.LoadTesting do + use Mix.Task + use Pleroma.LoadTesting.Helper + import Mix.Pleroma + import Pleroma.LoadTesting.Generator + import Pleroma.LoadTesting.Fetcher + + # tODO: remove autovacuum worker until generation is not ended + @shortdoc "Factory for generation data" + @moduledoc """ + Generates data like: + - users + - activities with notifications + + ## Generate data + MIX_ENV=test mix pleroma.load_testing --users 10000 --activities 20000 + MIX_ENV=test mix pleroma.load_testing -u 10000 -a 20000 + + Options: + - `--users NUMBER` - number of users to generate (default: 10000) + - `--activities NUMBER` - number of activities to generate (default: 20000) + """ + + @aliases [u: :users, a: :activities, d: :delete] + @switches [users: :integer, activities: :integer, delete: :boolean] + @users_default 20_000 + @activities_default 50_000 + + def run(args) do + {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) + start_pleroma() + + current_max = Keyword.get(opts, :users, @users_default) + activities_max = Keyword.get(opts, :activities, @activities_default) + + {users_min, users_max} = + if opts[:delete] do + clean_tables() + {1, current_max} + else + current_count = Repo.aggregate(from(u in User), :count, :id) + 1 + {current_count, current_max + current_count} + end + + opts = + Keyword.put(opts, :users_min, users_min) + |> Keyword.put(:users_max, users_max) + |> Keyword.put(:activities_max, activities_max) + + generate_users(opts) + + # main user for queries + IO.puts("Fetching main user...") + + {time, user} = + :timer.tc(fn -> Repo.one(from(u in User, order_by: fragment("RANDOM()"), limit: 1)) end) + + IO.puts("Fetching main user take #{to_sec(time)} sec.\n") + + IO.puts("Fetching users...") + + {time, users} = + :timer.tc(fn -> + Repo.all( + from(u in User, + where: u.id != ^user.id, + order_by: fragment("RANDOM()"), + limit: 10 + ) + ) + end) + + IO.puts("Fetching users take #{to_sec(time)} sec.\n") + + generate_activities(users, opts) + + generate_activities(users, Keyword.put(opts, :mention, user)) + + # generate_replies(user, users, activities) + + # activity = Enum.random(activities) + # generate_long_thread(user, users, activity) + + IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}") + IO.puts("Activities in DB: #{Repo.aggregate(from(a in Activity), :count, :id)}") + IO.puts("Objects in DB: #{Repo.aggregate(from(o in Object), :count, :id)}") + IO.puts("Notifications in DB: #{Repo.aggregate(from(n in Notification), :count, :id)}") + + query_timelines(user) + query_notifications(user) + # query_long_thread(user, activity) + end + + defp clean_tables do + IO.puts("\n\nDeleting old data...\n") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;") + end +end -- cgit v1.2.3 From 79dde5804427ab24c5bc9aa1b5a44d2259cecc6d Mon Sep 17 00:00:00 2001 From: Alex S Date: Wed, 4 Sep 2019 20:18:11 +0300 Subject: one more temp commit --- lib/load_testing/fetcher.ex | 122 ++++++++++++------------- lib/load_testing/generator.ex | 127 ++++++++++++++++++++++----- lib/mix/tasks/pleroma/load_testing.ex | 46 ++++++---- lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++ 4 files changed, 203 insertions(+), 97 deletions(-) (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex index 70c0fcd0c..ed744db9b 100644 --- a/lib/load_testing/fetcher.ex +++ b/lib/load_testing/fetcher.ex @@ -4,33 +4,18 @@ defmodule Pleroma.LoadTesting.Fetcher do def fetch_user(user) do IO.puts("=================================") - {time, _value} = :timer.tc(fn -> Repo.get_by(User, id: user.id) end) - - IO.puts("Query user by id: #{to_sec(time)} sec.") - - {time, _value} = - :timer.tc(fn -> - Repo.get_by(User, ap_id: user.ap_id) - end) - - IO.puts("Query user by ap_id: #{to_sec(time)} sec.") - - {time, _value} = - :timer.tc(fn -> - Repo.get_by(User, email: user.email) - end) - - IO.puts("Query user by email: #{to_sec(time)} sec.") - - {time, _value} = :timer.tc(fn -> Repo.get_by(User, nickname: user.nickname) end) - - IO.puts("Query user by nickname: #{to_sec(time)} sec.") + Benchee.run(%{ + "By id" => fn -> Repo.get_by(User, id: user.id) end, + "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end, + "By email" => fn -> Repo.get_by(User, email: user.email) end, + "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end + }) end def query_timelines(user) do IO.puts("\n=================================") - params = %{ + home_timeline_params = %{ "count" => 20, "with_muted" => true, "type" => ["Create", "Announce"], @@ -39,14 +24,7 @@ defmodule Pleroma.LoadTesting.Fetcher do "user" => user } - {time, _} = - :timer.tc(fn -> - ActivityPub.ActivityPub.fetch_activities([user.ap_id | user.following], params) - end) - - IO.puts("Query user home timeline: #{to_sec(time)} sec.") - - params = %{ + mastodon_public_timeline_params = %{ "count" => 20, "local_only" => true, "only_media" => "false", @@ -56,14 +34,7 @@ defmodule Pleroma.LoadTesting.Fetcher do "muting_user" => user } - {time, _} = - :timer.tc(fn -> - ActivityPub.ActivityPub.fetch_public_activities(params) - end) - - IO.puts("Query user mastodon public timeline: #{to_sec(time)} sec.") - - params = %{ + mastodon_federated_timeline_params = %{ "count" => 20, "only_media" => "false", "type" => ["Create", "Announce"], @@ -72,45 +43,76 @@ defmodule Pleroma.LoadTesting.Fetcher do "muting_user" => user } - {time, _} = - :timer.tc(fn -> - ActivityPub.ActivityPub.fetch_public_activities(params) - end) - - IO.puts("Query user mastodon federated public timeline: #{to_sec(time)} sec.") + Benchee.run(%{ + "User home timeline" => fn -> + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( + [user.ap_id | user.following], + home_timeline_params + ) + end, + "User mastodon public timeline" => fn -> + ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params) + end, + "User mastodon federated public timeline" => fn -> + ActivityPub.ActivityPub.fetch_public_activities(mastodon_federated_timeline_params) + end + }) end def query_notifications(user) do IO.puts("\n=================================") - params = %{"count" => "20", "with_muted" => "false"} - - {time, _} = - :timer.tc(fn -> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, params) end) - - IO.puts("Query user notifications with out muted: #{to_sec(time)} sec.") + without_muted_params = %{"count" => "20", "with_muted" => "false"} + with_muted_params = %{"count" => "20", "with_muted" => "true"} + + Benchee.run(%{ + "Notifications without muted" => fn -> + Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params) + end, + "Notifications with muted" => fn -> + Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params) + end + }) + end - params = %{"count" => "20", "with_muted" => "true"} + def query_dms(user) do + IO.puts("\n=================================") - {time, _} = - :timer.tc(fn -> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, params) end) + params = %{ + "count" => "20", + "with_muted" => "true", + "type" => "Create", + "blocking_user" => user, + "user" => user, + visibility: "direct" + } - IO.puts("Query user notifications with muted: #{to_sec(time)} sec.") + Benchee.run(%{ + "Direct messages with muted" => fn -> + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) + |> Pleroma.Pagination.fetch_paginated(params) + end, + "Direct messages without muted" => fn -> + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false)) + end + }) end def query_long_thread(user, activity) do IO.puts("\n=================================") - {time, replies} = - :timer.tc(fn -> + Benchee.run(%{ + "Fetch main post" => fn -> Activity.get_by_id_with_object(activity.id) end, + "Fetch context of main post" => fn -> Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( activity.data["context"], %{ "blocking_user" => user, - "user" => user + "user" => user, + "exclude_id" => activity.id } ) - end) - - IO.puts("Query long thread with #{length(replies)} replies: #{to_sec(time)} sec.") + end + }) end end diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex index a9016b9e8..7f50ee68e 100644 --- a/lib/load_testing/generator.ex +++ b/lib/load_testing/generator.ex @@ -8,25 +8,15 @@ defmodule Pleroma.LoadTesting.Generator do end defp do_generate_users(opts) do - min = Keyword.get(opts, :users_min, 1) max = Keyword.get(opts, :users_max) - query = - "INSERT INTO \"users\" (\"ap_id\",\"bio\",\"email\",\"follower_address\",\"following\",\"following_address\",\"info\", - \"local\",\"name\",\"nickname\",\"password_hash\",\"tags\",\"id\",\"inserted_at\",\"updated_at\") VALUES \n" - - users = - Task.async_stream( - min..max, - &generate_user_data(&1), - max_concurrency: 10, - timeout: 30_000 - ) - |> Enum.reduce("", fn {:ok, data}, acc -> acc <> data <> ", \n" end) - - query = query <> String.replace_trailing(users, ", \n", ";") - - Ecto.Adapters.SQL.query!(Repo, query) + Task.async_stream( + 1..max, + &generate_user_data(&1), + max_concurrency: 10, + timeout: 30_000 + ) + |> Enum.to_list() end defp generate_user_data(i) do @@ -47,11 +37,7 @@ defmodule Pleroma.LoadTesting.Generator do following: [User.ap_id(user)] } - "('#{user.ap_id}', '#{user.bio}', '#{user.email}', '#{user.follower_address}', '{#{ - user.following - }}', '#{user.following_address}', '#{Jason.encode!(user.info)}', '#{user.local}', '#{ - user.name - }', '#{user.nickname}', '#{user.password_hash}', '{#{user.tags}}', uuid_generate_v4(), NOW(), NOW())" + Pleroma.Repo.insert!(user) end def generate_activities(users, opts) do @@ -80,4 +66,101 @@ defmodule Pleroma.LoadTesting.Generator do Pleroma.Web.CommonAPI.post(Enum.random(users), %{"status" => status}) end + + def generate_dms(user, users, opts) do + IO.puts("Starting generating #{opts[:dms_max]} DMs") + {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end) + IO.puts("Inserting dms take #{to_sec(time)} sec.\n") + end + + defp do_generate_dms(user, users, opts) do + Task.async_stream( + 1..opts[:dms_max], + fn _ -> + do_generate_dm(user, users) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end + + defp do_generate_dm(user, users) do + post = %{ + "status" => "@#{user.nickname} some direct message", + "visibility" => "direct" + } + + Pleroma.Web.CommonAPI.post(Enum.random(users), post) + end + + def generate_long_thread(user, users, opts) do + IO.puts("Starting generating long thread with #{opts[:long_thread_length]} replies") + {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end) + IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n") + {:ok, activity} + end + + defp do_generate_long_thread(user, users, opts) do + {:ok, %{id: id} = activity} = + Pleroma.Web.CommonAPI.post(user, %{"status" => "Start of long thread"}) + + Task.async_stream( + 1..opts[:long_thread_length], + fn _ -> do_generate_thread(users, id) end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + + activity + end + + defp do_generate_thread(users, activity_id) do + Pleroma.Web.CommonAPI.post(Enum.random(users), %{ + "status" => "reply to main post", + "in_reply_to_status_id" => activity_id + }) + end + + def generate_private_thread(users, opts) do + IO.puts("Starting generating long thread with #{opts[:non_visible_posts_max]} replies") + {time, _} = :timer.tc(fn -> do_generate_non_visible_posts(users, opts) end) + IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n") + end + + defp do_generate_non_visible_posts(users, opts) do + [user1, user2] = Enum.take(users, 2) + {:ok, user1} = Pleroma.User.follow(user1, user2) + {:ok, user2} = Pleroma.User.follow(user2, user1) + + {:ok, activity} = + Pleroma.Web.CommonAPI.post(user1, %{ + "status" => "Some private post", + "visibility" => "private" + }) + + {:ok, activity_public} = + Pleroma.Web.CommonAPI.post(user2, %{ + "status" => "Some public reply", + "in_reply_to_status_id" => activity.id + }) + + Task.async_stream( + 1..opts[:non_visible_posts_max], + fn _ -> do_generate_non_visible_post(users, activity_public) end, + max_concurrency: 10, + timeout: 30_000 + ) + end + + defp do_generate_non_visible_post(users, activity) do + visibility = Enum.random(["private", "public"]) + + Pleroma.Web.CommonAPI.post(Enum.random(users), %{ + "visibility" => visibility, + "status" => "Some #{visibility} reply", + "in_reply_to_status_id" => activity.id + }) + end end diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex index 9ed30db7e..d17cf0b3e 100644 --- a/lib/mix/tasks/pleroma/load_testing.ex +++ b/lib/mix/tasks/pleroma/load_testing.ex @@ -21,31 +21,38 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do - `--activities NUMBER` - number of activities to generate (default: 20000) """ - @aliases [u: :users, a: :activities, d: :delete] - @switches [users: :integer, activities: :integer, delete: :boolean] + @aliases [u: :users, a: :activities] + @switches [ + users: :integer, + activities: :integer, + dms: :integer, + thread_length: :integer, + non_visible_posts: :integer + ] @users_default 20_000 @activities_default 50_000 + @dms_default 50_000 + @thread_length_default 2_000 + @non_visible_posts_default 2_000 def run(args) do - {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) start_pleroma() + {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) - current_max = Keyword.get(opts, :users, @users_default) + users_max = Keyword.get(opts, :users, @users_default) activities_max = Keyword.get(opts, :activities, @activities_default) + dms_max = Keyword.get(opts, :dms, @dms_default) + long_thread_length = Keyword.get(opts, :thread_length, @thread_length_default) + non_visible_posts = Keyword.get(opts, :non_visible_posts, @non_visible_posts_default) - {users_min, users_max} = - if opts[:delete] do - clean_tables() - {1, current_max} - else - current_count = Repo.aggregate(from(u in User), :count, :id) + 1 - {current_count, current_max + current_count} - end + clean_tables() opts = - Keyword.put(opts, :users_min, users_min) - |> Keyword.put(:users_max, users_max) + Keyword.put(opts, :users_max, users_max) |> Keyword.put(:activities_max, activities_max) + |> Keyword.put(:dms_max, dms_max) + |> Keyword.put(:long_thread_length, long_thread_length) + |> Keyword.put(:non_visible_posts_max, non_visible_posts) generate_users(opts) @@ -76,6 +83,12 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do generate_activities(users, Keyword.put(opts, :mention, user)) + generate_dms(user, users, opts) + + {:ok, activity} = generate_long_thread(user, users, opts) + + generate_private_thread(users, opts) + # generate_replies(user, users, activities) # activity = Enum.random(activities) @@ -86,9 +99,12 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do IO.puts("Objects in DB: #{Repo.aggregate(from(o in Object), :count, :id)}") IO.puts("Notifications in DB: #{Repo.aggregate(from(n in Notification), :count, :id)}") + fetch_user(user) query_timelines(user) query_notifications(user) - # query_long_thread(user, activity) + query_dms(user) + query_long_thread(user, activity) + query_timelines(user) end defp clean_tables do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index eeb826814..f2b322314 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -249,6 +249,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do # only accept false as false value local = !(params[:local] == false) published = params[:published] + quick_insert? = Pleroma.Config.get([:env]) == :benchmark with create_data <- make_create_data( @@ -259,12 +260,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), _ <- increase_poll_votes_if_vote(create_data), + {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, # Changing note count prior to enqueuing federation task in order to avoid # race conditions on updating user.info {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} else + {:quick_insert, true, activity} -> + {:ok, activity} + {:fake, true, activity} -> {:ok, activity} -- cgit v1.2.3 From a1125bd5644a5819d1cfbbc8f6b3bb2dadbbc38a Mon Sep 17 00:00:00 2001 From: Alex S Date: Thu, 5 Sep 2019 16:01:52 +0300 Subject: generatoin and fetching --- lib/load_testing/fetcher.ex | 126 ++++++++++++++++++++- lib/load_testing/generator.ex | 203 ++++++++++++++++++++++++---------- lib/load_testing/helper.ex | 5 - lib/mix/tasks/pleroma/load_testing.ex | 47 ++++---- 4 files changed, 289 insertions(+), 92 deletions(-) (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex index ed744db9b..825f921e6 100644 --- a/lib/load_testing/fetcher.ex +++ b/lib/load_testing/fetcher.ex @@ -51,10 +51,52 @@ defmodule Pleroma.LoadTesting.Fetcher do ) end, "User mastodon public timeline" => fn -> - ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params) + Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( + mastodon_public_timeline_params + ) end, "User mastodon federated public timeline" => fn -> - ActivityPub.ActivityPub.fetch_public_activities(mastodon_federated_timeline_params) + Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( + mastodon_federated_timeline_params + ) + end + }) + + home_activities = + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( + [user.ap_id | user.following], + home_timeline_params + ) + + public_activities = + Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params) + + public_federated_activities = + Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( + mastodon_federated_timeline_params + ) + + Benchee.run(%{ + "Rendering home timeline" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: home_activities, + for: user, + as: :activity + }) + end, + "Rendering public timeline" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: public_activities, + for: user, + as: :activity + }) + end, + "Rendering public federated timeline" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: public_federated_activities, + for: user, + as: :activity + }) end }) end @@ -72,6 +114,27 @@ defmodule Pleroma.LoadTesting.Fetcher do Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params) end }) + + without_muted_notifications = + Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params) + + with_muted_notifications = + Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params) + + Benchee.run(%{ + "Render notifications without muted" => fn -> + Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ + notifications: without_muted_notifications, + for: user + }) + end, + "Render notifications with muted" => fn -> + Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ + notifications: with_muted_notifications, + for: user + }) + end + }) end def query_dms(user) do @@ -96,13 +159,40 @@ defmodule Pleroma.LoadTesting.Fetcher do |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false)) end }) + + dms_with_muted = + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) + |> Pleroma.Pagination.fetch_paginated(params) + + dms_without_muted = + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false)) + + Benchee.run(%{ + "Rendering dms with muted" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: dms_with_muted, + for: user, + as: :activity + }) + end, + "Rendering dms without muted" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: dms_without_muted, + for: user, + as: :activity + }) + end + }) end def query_long_thread(user, activity) do IO.puts("\n=================================") Benchee.run(%{ - "Fetch main post" => fn -> Activity.get_by_id_with_object(activity.id) end, + "Fetch main post" => fn -> + Pleroma.Activity.get_by_id_with_object(activity.id) + end, "Fetch context of main post" => fn -> Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( activity.data["context"], @@ -114,5 +204,35 @@ defmodule Pleroma.LoadTesting.Fetcher do ) end }) + + activity = Pleroma.Activity.get_by_id_with_object(activity.id) + + context = + Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( + activity.data["context"], + %{ + "blocking_user" => user, + "user" => user, + "exclude_id" => activity.id + } + ) + + Benchee.run(%{ + "Render status" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{ + activity: activity, + for: user + }) + end, + "Render context ancestors" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render( + "index.json", + for: user, + activities: context, + as: :activity + ) + |> Enum.reverse() + end + }) end end diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex index 7f50ee68e..6df518aba 100644 --- a/lib/load_testing/generator.ex +++ b/lib/load_testing/generator.ex @@ -1,9 +1,11 @@ defmodule Pleroma.LoadTesting.Generator do use Pleroma.LoadTesting.Helper + alias Pleroma.Web.CommonAPI def generate_users(opts) do IO.puts("Starting generating #{opts[:users_max]} users...") {time, _} = :timer.tc(fn -> do_generate_users(opts) end) + IO.puts("Inserting users take #{to_sec(time)} sec.\n") end @@ -37,34 +39,111 @@ defmodule Pleroma.LoadTesting.Generator do following: [User.ap_id(user)] } - Pleroma.Repo.insert!(user) + Repo.insert!(user) end - def generate_activities(users, opts) do - IO.puts("Starting generating #{opts[:activities_max]} activities...") - {time, _} = :timer.tc(fn -> do_generate_activities(users, opts) end) - IO.puts("Inserting activities take #{to_sec(time)} sec.\n") + def generate_activities(user, users) do + do_generate_activities(user, users) end - defp do_generate_activities(users, opts) do - Task.async_stream( - 1..opts[:activities_max], - fn _ -> - do_generate_activity(users, opts) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() + defp do_generate_activities(user, users) do + IO.puts("Starting generating 20000 common activities...") + + {time, _} = + :timer.tc(fn -> + Task.async_stream( + 1..20_000, + fn _ -> + do_generate_activity([user | users]) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end) + + IO.puts("Inserting common activities take #{to_sec(time)} sec.\n") + + IO.puts("Starting generating 20000 activities with mentions...") + + {time, _} = + :timer.tc(fn -> + Task.async_stream( + 1..20_000, + fn _ -> + do_generate_activity_with_mention(user, users) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end) + + IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n") + + IO.puts("Starting generating 10000 activities with threads...") + + {time, _} = + :timer.tc(fn -> + Task.async_stream( + 1..10_000, + fn _ -> + do_generate_threads([user | users]) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end) + + IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n") + end + + defp do_generate_activity(users) do + post = %{ + "status" => "Some status without mention with random user" + } + + CommonAPI.post(Enum.random(users), post) + end + + defp do_generate_activity_with_mention(user, users) do + mentions_cnt = Enum.random([2, 3, 4, 5]) + with_user = Enum.random([true, false]) + users = Enum.shuffle(users) + mentions_users = Enum.take(users, mentions_cnt) + mentions_users = if with_user, do: [user | mentions_users], else: mentions_users + + mentions_str = + Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ") + + post = %{ + "status" => mentions_str <> "some status with mentions random users" + } + + CommonAPI.post(Enum.random(users), post) end - defp do_generate_activity(users, opts) do - status = - if opts[:mention], - do: "some status with @#{opts[:mention].nickname}", - else: "some status" + defp do_generate_threads(users) do + thread_length = Enum.random([2, 3, 4, 5]) + actor = Enum.random(users) + + post = %{ + "status" => "Start of the thread" + } + + {:ok, activity} = CommonAPI.post(actor, post) + + Enum.each(1..thread_length, fn _ -> + user = Enum.random(users) + + post = %{ + "status" => "@#{actor.nickname} reply to thread", + "in_reply_to_status_id" => activity.id + } - Pleroma.Web.CommonAPI.post(Enum.random(users), %{"status" => status}) + CommonAPI.post(user, post) + end) end def generate_dms(user, users, opts) do @@ -91,22 +170,21 @@ defmodule Pleroma.LoadTesting.Generator do "visibility" => "direct" } - Pleroma.Web.CommonAPI.post(Enum.random(users), post) + CommonAPI.post(Enum.random(users), post) end def generate_long_thread(user, users, opts) do - IO.puts("Starting generating long thread with #{opts[:long_thread_length]} replies") + IO.puts("Starting generating long thread with #{opts[:thread_length]} replies") {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end) IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n") {:ok, activity} end defp do_generate_long_thread(user, users, opts) do - {:ok, %{id: id} = activity} = - Pleroma.Web.CommonAPI.post(user, %{"status" => "Start of long thread"}) + {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"}) Task.async_stream( - 1..opts[:long_thread_length], + 1..opts[:thread_length], fn _ -> do_generate_thread(users, id) end, max_concurrency: 10, timeout: 30_000 @@ -117,50 +195,63 @@ defmodule Pleroma.LoadTesting.Generator do end defp do_generate_thread(users, activity_id) do - Pleroma.Web.CommonAPI.post(Enum.random(users), %{ + CommonAPI.post(Enum.random(users), %{ "status" => "reply to main post", "in_reply_to_status_id" => activity_id }) end - def generate_private_thread(users, opts) do - IO.puts("Starting generating long thread with #{opts[:non_visible_posts_max]} replies") - {time, _} = :timer.tc(fn -> do_generate_non_visible_posts(users, opts) end) - IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n") - end + def generate_non_visible_message(user, users) do + IO.puts("Starting generating 1000 non visible posts") - defp do_generate_non_visible_posts(users, opts) do - [user1, user2] = Enum.take(users, 2) - {:ok, user1} = Pleroma.User.follow(user1, user2) - {:ok, user2} = Pleroma.User.follow(user2, user1) + {time, _} = + :timer.tc(fn -> + do_generate_non_visible_posts(user, users) + end) - {:ok, activity} = - Pleroma.Web.CommonAPI.post(user1, %{ - "status" => "Some private post", - "visibility" => "private" - }) + IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n") + end - {:ok, activity_public} = - Pleroma.Web.CommonAPI.post(user2, %{ - "status" => "Some public reply", - "in_reply_to_status_id" => activity.id - }) + defp do_generate_non_visible_posts(user, users) do + [not_friend | users] = users - Task.async_stream( - 1..opts[:non_visible_posts_max], - fn _ -> do_generate_non_visible_post(users, activity_public) end, + make_friends(user, users) + + Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end, max_concurrency: 10, timeout: 30_000 ) + |> Stream.run() end - defp do_generate_non_visible_post(users, activity) do - visibility = Enum.random(["private", "public"]) + defp make_friends(_user, []), do: nil - Pleroma.Web.CommonAPI.post(Enum.random(users), %{ - "visibility" => visibility, - "status" => "Some #{visibility} reply", - "in_reply_to_status_id" => activity.id - }) + defp make_friends(user, [friend | users]) do + {:ok, _} = User.follow(user, friend) + {:ok, _} = User.follow(friend, user) + make_friends(user, users) + end + + defp do_generate_non_visible_post(not_friend, users) do + post = %{ + "status" => "some non visible post", + "visibility" => "private" + } + + {:ok, activity} = CommonAPI.post(not_friend, post) + + thread_length = Enum.random([2, 3, 4, 5]) + + Enum.each(1..thread_length, fn _ -> + user = Enum.random(users) + + post = %{ + "status" => "@#{not_friend.nickname} reply to non visible post", + "in_reply_to_status_id" => activity.id, + "visibility" => "private" + } + + CommonAPI.post(user, post) + end) end end diff --git a/lib/load_testing/helper.ex b/lib/load_testing/helper.ex index 338dba323..47b25c65f 100644 --- a/lib/load_testing/helper.ex +++ b/lib/load_testing/helper.ex @@ -2,13 +2,8 @@ defmodule Pleroma.LoadTesting.Helper do defmacro __using__(_) do quote do import Ecto.Query - alias Pleroma.Activity - alias Pleroma.Notification - alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.ActivityPub - alias Pleroma.Web.CommonAPI defp to_sec(microseconds), do: microseconds / 1_000_000 end diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex index d17cf0b3e..83e531abf 100644 --- a/lib/mix/tasks/pleroma/load_testing.ex +++ b/lib/mix/tasks/pleroma/load_testing.ex @@ -13,46 +13,37 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do - activities with notifications ## Generate data - MIX_ENV=test mix pleroma.load_testing --users 10000 --activities 20000 - MIX_ENV=test mix pleroma.load_testing -u 10000 -a 20000 + MIX_ENV=benchmark mix pleroma.load_testing --users 10000 + MIX_ENV=benchmark mix pleroma.load_testing -u 10000 Options: - `--users NUMBER` - number of users to generate (default: 10000) - - `--activities NUMBER` - number of activities to generate (default: 20000) """ - @aliases [u: :users, a: :activities] + @aliases [u: :users, d: :dms, t: :thread_length] @switches [ users: :integer, - activities: :integer, dms: :integer, - thread_length: :integer, - non_visible_posts: :integer + thread_length: :integer ] @users_default 20_000 - @activities_default 50_000 - @dms_default 50_000 + @dms_default 20_000 @thread_length_default 2_000 - @non_visible_posts_default 2_000 def run(args) do start_pleroma() {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) users_max = Keyword.get(opts, :users, @users_default) - activities_max = Keyword.get(opts, :activities, @activities_default) dms_max = Keyword.get(opts, :dms, @dms_default) - long_thread_length = Keyword.get(opts, :thread_length, @thread_length_default) - non_visible_posts = Keyword.get(opts, :non_visible_posts, @non_visible_posts_default) + thread_length = Keyword.get(opts, :thread_length, @thread_length_default) clean_tables() opts = Keyword.put(opts, :users_max, users_max) - |> Keyword.put(:activities_max, activities_max) |> Keyword.put(:dms_max, dms_max) - |> Keyword.put(:long_thread_length, long_thread_length) - |> Keyword.put(:non_visible_posts_max, non_visible_posts) + |> Keyword.put(:thread_length, thread_length) generate_users(opts) @@ -60,7 +51,9 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do IO.puts("Fetching main user...") {time, user} = - :timer.tc(fn -> Repo.one(from(u in User, order_by: fragment("RANDOM()"), limit: 1)) end) + :timer.tc(fn -> + Repo.one(from(u in User, order_by: fragment("RANDOM()"), limit: 1)) + end) IO.puts("Fetching main user take #{to_sec(time)} sec.\n") @@ -79,25 +72,23 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do IO.puts("Fetching users take #{to_sec(time)} sec.\n") - generate_activities(users, opts) - - generate_activities(users, Keyword.put(opts, :mention, user)) + generate_activities(user, users) generate_dms(user, users, opts) {:ok, activity} = generate_long_thread(user, users, opts) - generate_private_thread(users, opts) + generate_non_visible_message(user, users) - # generate_replies(user, users, activities) + IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}") - # activity = Enum.random(activities) - # generate_long_thread(user, users, activity) + IO.puts("Activities in DB: #{Repo.aggregate(from(a in Pleroma.Activity), :count, :id)}") - IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}") - IO.puts("Activities in DB: #{Repo.aggregate(from(a in Activity), :count, :id)}") - IO.puts("Objects in DB: #{Repo.aggregate(from(o in Object), :count, :id)}") - IO.puts("Notifications in DB: #{Repo.aggregate(from(n in Notification), :count, :id)}") + IO.puts("Objects in DB: #{Repo.aggregate(from(o in Pleroma.Object), :count, :id)}") + + IO.puts( + "Notifications in DB: #{Repo.aggregate(from(n in Pleroma.Notification), :count, :id)}" + ) fetch_user(user) query_timelines(user) -- cgit v1.2.3 From 252e5db45c5d9d63116e5a24cc35c26f91ec7de4 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 6 Sep 2019 16:37:18 +0300 Subject: docs fixes --- lib/load_testing/fetcher.ex | 9 --------- lib/load_testing/generator.ex | 3 ++- lib/mix/tasks/pleroma/load_testing.ex | 16 +++++++++++----- 3 files changed, 13 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex index 825f921e6..b92b6e04f 100644 --- a/lib/load_testing/fetcher.ex +++ b/lib/load_testing/fetcher.ex @@ -2,8 +2,6 @@ defmodule Pleroma.LoadTesting.Fetcher do use Pleroma.LoadTesting.Helper def fetch_user(user) do - IO.puts("=================================") - Benchee.run(%{ "By id" => fn -> Repo.get_by(User, id: user.id) end, "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end, @@ -13,8 +11,6 @@ defmodule Pleroma.LoadTesting.Fetcher do end def query_timelines(user) do - IO.puts("\n=================================") - home_timeline_params = %{ "count" => 20, "with_muted" => true, @@ -102,7 +98,6 @@ defmodule Pleroma.LoadTesting.Fetcher do end def query_notifications(user) do - IO.puts("\n=================================") without_muted_params = %{"count" => "20", "with_muted" => "false"} with_muted_params = %{"count" => "20", "with_muted" => "true"} @@ -138,8 +133,6 @@ defmodule Pleroma.LoadTesting.Fetcher do end def query_dms(user) do - IO.puts("\n=================================") - params = %{ "count" => "20", "with_muted" => "true", @@ -187,8 +180,6 @@ defmodule Pleroma.LoadTesting.Fetcher do end def query_long_thread(user, activity) do - IO.puts("\n=================================") - Benchee.run(%{ "Fetch main post" => fn -> Pleroma.Activity.get_by_id_with_object(activity.id) diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex index 6df518aba..7ad68dcc1 100644 --- a/lib/load_testing/generator.ex +++ b/lib/load_testing/generator.ex @@ -28,7 +28,8 @@ defmodule Pleroma.LoadTesting.Generator do nickname: "nick#{i}", password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), bio: "Tester Number #{i}", - info: %{} + info: %{}, + local: Enum.random([true, false]) } user = %{ diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex index 83e531abf..4fed6bf74 100644 --- a/lib/mix/tasks/pleroma/load_testing.ex +++ b/lib/mix/tasks/pleroma/load_testing.ex @@ -5,19 +5,23 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do import Pleroma.LoadTesting.Generator import Pleroma.LoadTesting.Fetcher - # tODO: remove autovacuum worker until generation is not ended @shortdoc "Factory for generation data" @moduledoc """ Generates data like: - users - activities with notifications + - direct messages + - long thread + - non visible posts ## Generate data - MIX_ENV=benchmark mix pleroma.load_testing --users 10000 - MIX_ENV=benchmark mix pleroma.load_testing -u 10000 + MIX_ENV=benchmark mix pleroma.load_testing --users 20000 --dms 20000 --thread_length 2000 + MIX_ENV=benchmark mix pleroma.load_testing -u 20000 -d 20000 -t 2000 Options: - - `--users NUMBER` - number of users to generate (default: 10000) + - `--users NUMBER` - number of users to generate. Defaults to: 20000. Alias: `-u` + - `--dms NUMBER` - number of direct messages to generate. Defaults to: 20000. Alias `-d` + - `--thread_length` - number of messages in thread. Defaults to: 2000. ALias `-t` """ @aliases [u: :users, d: :dms, t: :thread_length] @@ -32,6 +36,7 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do def run(args) do start_pleroma() + Pleroma.Config.put([:instance, :skip_thread_containment], true) {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) users_max = Keyword.get(opts, :users, @users_default) @@ -95,11 +100,12 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do query_notifications(user) query_dms(user) query_long_thread(user, activity) + Pleroma.Config.put([:instance, :skip_thread_containment], false) query_timelines(user) end defp clean_tables do - IO.puts("\n\nDeleting old data...\n") + IO.puts("Deleting old data...\n") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;") -- cgit v1.2.3 From b3f6f6a4091c048813e367f6a9414eba6fce5109 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 6 Sep 2019 20:14:02 +0300 Subject: generating remote users --- lib/load_testing/fetcher.ex | 2 +- lib/load_testing/generator.ex | 34 ++++++++++++++++++++++++++-------- lib/pleroma/user.ex | 4 +++- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex index b92b6e04f..0ff2f28c0 100644 --- a/lib/load_testing/fetcher.ex +++ b/lib/load_testing/fetcher.ex @@ -215,7 +215,7 @@ defmodule Pleroma.LoadTesting.Fetcher do for: user }) end, - "Render context ancestors" => fn -> + "Render context" => fn -> Pleroma.Web.MastodonAPI.StatusView.render( "index.json", for: user, diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex index 7ad68dcc1..61e3d8686 100644 --- a/lib/load_testing/generator.ex +++ b/lib/load_testing/generator.ex @@ -22,6 +22,8 @@ defmodule Pleroma.LoadTesting.Generator do end defp generate_user_data(i) do + remote = Enum.random([true, false]) + user = %User{ name: "Test テスト User #{i}", email: "user#{i}@example.com", @@ -29,16 +31,32 @@ defmodule Pleroma.LoadTesting.Generator do password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), bio: "Tester Number #{i}", info: %{}, - local: Enum.random([true, false]) + local: remote } - user = %{ - user - | ap_id: User.ap_id(user), - follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] - } + user_urls = + if remote do + base_url = + Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"]) + + ap_id = "#{base_url}/users/#{user.nickname}" + + %{ + ap_id: ap_id, + follower_address: ap_id <> "/followers", + following_address: ap_id <> "/following", + following: [ap_id] + } + else + %{ + ap_id: User.ap_id(user), + follower_address: User.ap_followers(user), + following_address: User.ap_following(user), + following: [User.ap_id(user)] + } + end + + user = Map.merge(user, user_urls) Repo.insert!(user) end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3aa245f2a..37e59a651 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -429,7 +429,9 @@ defmodule Pleroma.User do {:error, "Could not follow user: #{followed.nickname} blocked you."} true -> - if !followed.local && follower.local && !ap_enabled?(followed) do + benchmark? = Pleroma.Config.get([:env]) == :benchmark + + if !followed.local && follower.local && !ap_enabled?(followed) && !benchmark? do Websub.subscribe(follower, followed) end -- cgit v1.2.3 From 924d7e6aa60095e27c15b4a5f473a192ab2b42ef Mon Sep 17 00:00:00 2001 From: Alex S Date: Thu, 19 Sep 2019 12:59:36 +0300 Subject: generating remote activities --- lib/load_testing/generator.ex | 78 ++++++++++++++++++++++++++++++++++- lib/mix/tasks/pleroma/load_testing.ex | 29 +++++++++++-- 2 files changed, 102 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex index 61e3d8686..5c5a5c122 100644 --- a/lib/load_testing/generator.ex +++ b/lib/load_testing/generator.ex @@ -28,7 +28,8 @@ defmodule Pleroma.LoadTesting.Generator do name: "Test テスト User #{i}", email: "user#{i}@example.com", nickname: "nick#{i}", - password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), + password_hash: + "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg", bio: "Tester Number #{i}", info: %{}, local: remote @@ -165,6 +166,81 @@ defmodule Pleroma.LoadTesting.Generator do end) end + def generate_remote_activities(user, users) do + do_generate_remote_activities(user, users) + end + + defp do_generate_remote_activities(user, users) do + IO.puts("Starting generating 10000 remote activities...") + + {time, _} = + :timer.tc(fn -> + Task.async_stream( + 1..10_000, + fn i -> + do_generate_remote_activity(i, user, users) + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Stream.run() + end) + + IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n") + end + + defp do_generate_remote_activity(i, user, users) do + actor = Enum.random(users) + %{host: host} = URI.parse(actor.ap_id) + date = Date.utc_today() + datetime = DateTime.utc_now() + + map = %{ + "actor" => actor.ap_id, + "cc" => [actor.follower_address, user.ap_id], + "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", + "id" => actor.ap_id <> "/statuses/#{i}/activity", + "object" => %{ + "actor" => actor.ap_id, + "atomUri" => actor.ap_id <> "/statuses/#{i}", + "attachment" => [], + "attributedTo" => actor.ap_id, + "bcc" => [], + "bto" => [], + "cc" => [actor.follower_address, user.ap_id], + "content" => + "

+ user.ap_id <> + "\" class=\"u-url mention\">@" <> user.nickname <> "

", + "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", + "conversation" => + "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", + "emoji" => %{}, + "id" => actor.ap_id <> "/statuses/#{i}", + "inReplyTo" => nil, + "inReplyToAtomUri" => nil, + "published" => datetime, + "sensitive" => true, + "summary" => "cw", + "tag" => [ + %{ + "href" => user.ap_id, + "name" => "@#{user.nickname}@#{host}", + "type" => "Mention" + } + ], + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Note", + "url" => "http://#{host}/@#{actor.nickname}/#{i}" + }, + "published" => datetime, + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create" + } + + Pleroma.Web.ActivityPub.ActivityPub.insert(map, false) + end + def generate_dms(user, users, opts) do IO.puts("Starting generating #{opts[:dms_max]} DMs") {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end) diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex index 4fed6bf74..a7057395d 100644 --- a/lib/mix/tasks/pleroma/load_testing.ex +++ b/lib/mix/tasks/pleroma/load_testing.ex @@ -53,32 +53,53 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do generate_users(opts) # main user for queries - IO.puts("Fetching main user...") + IO.puts("Fetching local main user...") {time, user} = :timer.tc(fn -> - Repo.one(from(u in User, order_by: fragment("RANDOM()"), limit: 1)) + Repo.one( + from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1) + ) end) IO.puts("Fetching main user take #{to_sec(time)} sec.\n") - IO.puts("Fetching users...") + IO.puts("Fetching local users...") {time, users} = :timer.tc(fn -> Repo.all( from(u in User, where: u.id != ^user.id, + where: u.local == true, + order_by: fragment("RANDOM()"), + limit: 10 + ) + ) + end) + + IO.puts("Fetching local users take #{to_sec(time)} sec.\n") + + IO.puts("Fetching remote users...") + + {time, remote_users} = + :timer.tc(fn -> + Repo.all( + from(u in User, + where: u.id != ^user.id, + where: u.local == false, order_by: fragment("RANDOM()"), limit: 10 ) ) end) - IO.puts("Fetching users take #{to_sec(time)} sec.\n") + IO.puts("Fetching remote users take #{to_sec(time)} sec.\n") generate_activities(user, users) + generate_remote_activities(user, remote_users) + generate_dms(user, users, opts) {:ok, activity} = generate_long_thread(user, users, opts) -- cgit v1.2.3 From 1d285e6fada841b6959b6516aca1c84d1a2c9b93 Mon Sep 17 00:00:00 2001 From: Alex S Date: Thu, 19 Sep 2019 14:02:27 +0300 Subject: moving to separate dir --- lib/load_testing/fetcher.ex | 229 ---------------------- lib/load_testing/generator.ex | 352 ---------------------------------- lib/load_testing/helper.ex | 11 -- lib/mix/tasks/pleroma/load_testing.ex | 134 ------------- 4 files changed, 726 deletions(-) delete mode 100644 lib/load_testing/fetcher.ex delete mode 100644 lib/load_testing/generator.ex delete mode 100644 lib/load_testing/helper.ex delete mode 100644 lib/mix/tasks/pleroma/load_testing.ex (limited to 'lib') diff --git a/lib/load_testing/fetcher.ex b/lib/load_testing/fetcher.ex deleted file mode 100644 index 0ff2f28c0..000000000 --- a/lib/load_testing/fetcher.ex +++ /dev/null @@ -1,229 +0,0 @@ -defmodule Pleroma.LoadTesting.Fetcher do - use Pleroma.LoadTesting.Helper - - def fetch_user(user) do - Benchee.run(%{ - "By id" => fn -> Repo.get_by(User, id: user.id) end, - "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end, - "By email" => fn -> Repo.get_by(User, email: user.email) end, - "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end - }) - end - - def query_timelines(user) do - home_timeline_params = %{ - "count" => 20, - "with_muted" => true, - "type" => ["Create", "Announce"], - "blocking_user" => user, - "muting_user" => user, - "user" => user - } - - mastodon_public_timeline_params = %{ - "count" => 20, - "local_only" => true, - "only_media" => "false", - "type" => ["Create", "Announce"], - "with_muted" => "true", - "blocking_user" => user, - "muting_user" => user - } - - mastodon_federated_timeline_params = %{ - "count" => 20, - "only_media" => "false", - "type" => ["Create", "Announce"], - "with_muted" => "true", - "blocking_user" => user, - "muting_user" => user - } - - Benchee.run(%{ - "User home timeline" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], - home_timeline_params - ) - end, - "User mastodon public timeline" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( - mastodon_public_timeline_params - ) - end, - "User mastodon federated public timeline" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( - mastodon_federated_timeline_params - ) - end - }) - - home_activities = - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], - home_timeline_params - ) - - public_activities = - Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params) - - public_federated_activities = - Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities( - mastodon_federated_timeline_params - ) - - Benchee.run(%{ - "Rendering home timeline" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: home_activities, - for: user, - as: :activity - }) - end, - "Rendering public timeline" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: public_activities, - for: user, - as: :activity - }) - end, - "Rendering public federated timeline" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: public_federated_activities, - for: user, - as: :activity - }) - end - }) - end - - def query_notifications(user) do - without_muted_params = %{"count" => "20", "with_muted" => "false"} - with_muted_params = %{"count" => "20", "with_muted" => "true"} - - Benchee.run(%{ - "Notifications without muted" => fn -> - Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params) - end, - "Notifications with muted" => fn -> - Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params) - end - }) - - without_muted_notifications = - Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params) - - with_muted_notifications = - Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params) - - Benchee.run(%{ - "Render notifications without muted" => fn -> - Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ - notifications: without_muted_notifications, - for: user - }) - end, - "Render notifications with muted" => fn -> - Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ - notifications: with_muted_notifications, - for: user - }) - end - }) - end - - def query_dms(user) do - params = %{ - "count" => "20", - "with_muted" => "true", - "type" => "Create", - "blocking_user" => user, - "user" => user, - visibility: "direct" - } - - Benchee.run(%{ - "Direct messages with muted" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) - |> Pleroma.Pagination.fetch_paginated(params) - end, - "Direct messages without muted" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) - |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false)) - end - }) - - dms_with_muted = - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) - |> Pleroma.Pagination.fetch_paginated(params) - - dms_without_muted = - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params) - |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false)) - - Benchee.run(%{ - "Rendering dms with muted" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: dms_with_muted, - for: user, - as: :activity - }) - end, - "Rendering dms without muted" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: dms_without_muted, - for: user, - as: :activity - }) - end - }) - end - - def query_long_thread(user, activity) do - Benchee.run(%{ - "Fetch main post" => fn -> - Pleroma.Activity.get_by_id_with_object(activity.id) - end, - "Fetch context of main post" => fn -> - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( - activity.data["context"], - %{ - "blocking_user" => user, - "user" => user, - "exclude_id" => activity.id - } - ) - end - }) - - activity = Pleroma.Activity.get_by_id_with_object(activity.id) - - context = - Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context( - activity.data["context"], - %{ - "blocking_user" => user, - "user" => user, - "exclude_id" => activity.id - } - ) - - Benchee.run(%{ - "Render status" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{ - activity: activity, - for: user - }) - end, - "Render context" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render( - "index.json", - for: user, - activities: context, - as: :activity - ) - |> Enum.reverse() - end - }) - end -end diff --git a/lib/load_testing/generator.ex b/lib/load_testing/generator.ex deleted file mode 100644 index 5c5a5c122..000000000 --- a/lib/load_testing/generator.ex +++ /dev/null @@ -1,352 +0,0 @@ -defmodule Pleroma.LoadTesting.Generator do - use Pleroma.LoadTesting.Helper - alias Pleroma.Web.CommonAPI - - def generate_users(opts) do - IO.puts("Starting generating #{opts[:users_max]} users...") - {time, _} = :timer.tc(fn -> do_generate_users(opts) end) - - IO.puts("Inserting users take #{to_sec(time)} sec.\n") - end - - defp do_generate_users(opts) do - max = Keyword.get(opts, :users_max) - - Task.async_stream( - 1..max, - &generate_user_data(&1), - max_concurrency: 10, - timeout: 30_000 - ) - |> Enum.to_list() - end - - defp generate_user_data(i) do - remote = Enum.random([true, false]) - - user = %User{ - name: "Test テスト User #{i}", - email: "user#{i}@example.com", - nickname: "nick#{i}", - password_hash: - "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg", - bio: "Tester Number #{i}", - info: %{}, - local: remote - } - - user_urls = - if remote do - base_url = - Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"]) - - ap_id = "#{base_url}/users/#{user.nickname}" - - %{ - ap_id: ap_id, - follower_address: ap_id <> "/followers", - following_address: ap_id <> "/following", - following: [ap_id] - } - else - %{ - ap_id: User.ap_id(user), - follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] - } - end - - user = Map.merge(user, user_urls) - - Repo.insert!(user) - end - - def generate_activities(user, users) do - do_generate_activities(user, users) - end - - defp do_generate_activities(user, users) do - IO.puts("Starting generating 20000 common activities...") - - {time, _} = - :timer.tc(fn -> - Task.async_stream( - 1..20_000, - fn _ -> - do_generate_activity([user | users]) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end) - - IO.puts("Inserting common activities take #{to_sec(time)} sec.\n") - - IO.puts("Starting generating 20000 activities with mentions...") - - {time, _} = - :timer.tc(fn -> - Task.async_stream( - 1..20_000, - fn _ -> - do_generate_activity_with_mention(user, users) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end) - - IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n") - - IO.puts("Starting generating 10000 activities with threads...") - - {time, _} = - :timer.tc(fn -> - Task.async_stream( - 1..10_000, - fn _ -> - do_generate_threads([user | users]) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end) - - IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n") - end - - defp do_generate_activity(users) do - post = %{ - "status" => "Some status without mention with random user" - } - - CommonAPI.post(Enum.random(users), post) - end - - defp do_generate_activity_with_mention(user, users) do - mentions_cnt = Enum.random([2, 3, 4, 5]) - with_user = Enum.random([true, false]) - users = Enum.shuffle(users) - mentions_users = Enum.take(users, mentions_cnt) - mentions_users = if with_user, do: [user | mentions_users], else: mentions_users - - mentions_str = - Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ") - - post = %{ - "status" => mentions_str <> "some status with mentions random users" - } - - CommonAPI.post(Enum.random(users), post) - end - - defp do_generate_threads(users) do - thread_length = Enum.random([2, 3, 4, 5]) - actor = Enum.random(users) - - post = %{ - "status" => "Start of the thread" - } - - {:ok, activity} = CommonAPI.post(actor, post) - - Enum.each(1..thread_length, fn _ -> - user = Enum.random(users) - - post = %{ - "status" => "@#{actor.nickname} reply to thread", - "in_reply_to_status_id" => activity.id - } - - CommonAPI.post(user, post) - end) - end - - def generate_remote_activities(user, users) do - do_generate_remote_activities(user, users) - end - - defp do_generate_remote_activities(user, users) do - IO.puts("Starting generating 10000 remote activities...") - - {time, _} = - :timer.tc(fn -> - Task.async_stream( - 1..10_000, - fn i -> - do_generate_remote_activity(i, user, users) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end) - - IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n") - end - - defp do_generate_remote_activity(i, user, users) do - actor = Enum.random(users) - %{host: host} = URI.parse(actor.ap_id) - date = Date.utc_today() - datetime = DateTime.utc_now() - - map = %{ - "actor" => actor.ap_id, - "cc" => [actor.follower_address, user.ap_id], - "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", - "id" => actor.ap_id <> "/statuses/#{i}/activity", - "object" => %{ - "actor" => actor.ap_id, - "atomUri" => actor.ap_id <> "/statuses/#{i}", - "attachment" => [], - "attributedTo" => actor.ap_id, - "bcc" => [], - "bto" => [], - "cc" => [actor.follower_address, user.ap_id], - "content" => - "

- user.ap_id <> - "\" class=\"u-url mention\">@" <> user.nickname <> "

", - "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", - "conversation" => - "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation", - "emoji" => %{}, - "id" => actor.ap_id <> "/statuses/#{i}", - "inReplyTo" => nil, - "inReplyToAtomUri" => nil, - "published" => datetime, - "sensitive" => true, - "summary" => "cw", - "tag" => [ - %{ - "href" => user.ap_id, - "name" => "@#{user.nickname}@#{host}", - "type" => "Mention" - } - ], - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Note", - "url" => "http://#{host}/@#{actor.nickname}/#{i}" - }, - "published" => datetime, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Create" - } - - Pleroma.Web.ActivityPub.ActivityPub.insert(map, false) - end - - def generate_dms(user, users, opts) do - IO.puts("Starting generating #{opts[:dms_max]} DMs") - {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end) - IO.puts("Inserting dms take #{to_sec(time)} sec.\n") - end - - defp do_generate_dms(user, users, opts) do - Task.async_stream( - 1..opts[:dms_max], - fn _ -> - do_generate_dm(user, users) - end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end - - defp do_generate_dm(user, users) do - post = %{ - "status" => "@#{user.nickname} some direct message", - "visibility" => "direct" - } - - CommonAPI.post(Enum.random(users), post) - end - - def generate_long_thread(user, users, opts) do - IO.puts("Starting generating long thread with #{opts[:thread_length]} replies") - {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end) - IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n") - {:ok, activity} - end - - defp do_generate_long_thread(user, users, opts) do - {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"}) - - Task.async_stream( - 1..opts[:thread_length], - fn _ -> do_generate_thread(users, id) end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - - activity - end - - defp do_generate_thread(users, activity_id) do - CommonAPI.post(Enum.random(users), %{ - "status" => "reply to main post", - "in_reply_to_status_id" => activity_id - }) - end - - def generate_non_visible_message(user, users) do - IO.puts("Starting generating 1000 non visible posts") - - {time, _} = - :timer.tc(fn -> - do_generate_non_visible_posts(user, users) - end) - - IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n") - end - - defp do_generate_non_visible_posts(user, users) do - [not_friend | users] = users - - make_friends(user, users) - - Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end, - max_concurrency: 10, - timeout: 30_000 - ) - |> Stream.run() - end - - defp make_friends(_user, []), do: nil - - defp make_friends(user, [friend | users]) do - {:ok, _} = User.follow(user, friend) - {:ok, _} = User.follow(friend, user) - make_friends(user, users) - end - - defp do_generate_non_visible_post(not_friend, users) do - post = %{ - "status" => "some non visible post", - "visibility" => "private" - } - - {:ok, activity} = CommonAPI.post(not_friend, post) - - thread_length = Enum.random([2, 3, 4, 5]) - - Enum.each(1..thread_length, fn _ -> - user = Enum.random(users) - - post = %{ - "status" => "@#{not_friend.nickname} reply to non visible post", - "in_reply_to_status_id" => activity.id, - "visibility" => "private" - } - - CommonAPI.post(user, post) - end) - end -end diff --git a/lib/load_testing/helper.ex b/lib/load_testing/helper.ex deleted file mode 100644 index 47b25c65f..000000000 --- a/lib/load_testing/helper.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Pleroma.LoadTesting.Helper do - defmacro __using__(_) do - quote do - import Ecto.Query - alias Pleroma.Repo - alias Pleroma.User - - defp to_sec(microseconds), do: microseconds / 1_000_000 - end - end -end diff --git a/lib/mix/tasks/pleroma/load_testing.ex b/lib/mix/tasks/pleroma/load_testing.ex deleted file mode 100644 index a7057395d..000000000 --- a/lib/mix/tasks/pleroma/load_testing.ex +++ /dev/null @@ -1,134 +0,0 @@ -defmodule Mix.Tasks.Pleroma.LoadTesting do - use Mix.Task - use Pleroma.LoadTesting.Helper - import Mix.Pleroma - import Pleroma.LoadTesting.Generator - import Pleroma.LoadTesting.Fetcher - - @shortdoc "Factory for generation data" - @moduledoc """ - Generates data like: - - users - - activities with notifications - - direct messages - - long thread - - non visible posts - - ## Generate data - MIX_ENV=benchmark mix pleroma.load_testing --users 20000 --dms 20000 --thread_length 2000 - MIX_ENV=benchmark mix pleroma.load_testing -u 20000 -d 20000 -t 2000 - - Options: - - `--users NUMBER` - number of users to generate. Defaults to: 20000. Alias: `-u` - - `--dms NUMBER` - number of direct messages to generate. Defaults to: 20000. Alias `-d` - - `--thread_length` - number of messages in thread. Defaults to: 2000. ALias `-t` - """ - - @aliases [u: :users, d: :dms, t: :thread_length] - @switches [ - users: :integer, - dms: :integer, - thread_length: :integer - ] - @users_default 20_000 - @dms_default 20_000 - @thread_length_default 2_000 - - def run(args) do - start_pleroma() - Pleroma.Config.put([:instance, :skip_thread_containment], true) - {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) - - users_max = Keyword.get(opts, :users, @users_default) - dms_max = Keyword.get(opts, :dms, @dms_default) - thread_length = Keyword.get(opts, :thread_length, @thread_length_default) - - clean_tables() - - opts = - Keyword.put(opts, :users_max, users_max) - |> Keyword.put(:dms_max, dms_max) - |> Keyword.put(:thread_length, thread_length) - - generate_users(opts) - - # main user for queries - IO.puts("Fetching local main user...") - - {time, user} = - :timer.tc(fn -> - Repo.one( - from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1) - ) - end) - - IO.puts("Fetching main user take #{to_sec(time)} sec.\n") - - IO.puts("Fetching local users...") - - {time, users} = - :timer.tc(fn -> - Repo.all( - from(u in User, - where: u.id != ^user.id, - where: u.local == true, - order_by: fragment("RANDOM()"), - limit: 10 - ) - ) - end) - - IO.puts("Fetching local users take #{to_sec(time)} sec.\n") - - IO.puts("Fetching remote users...") - - {time, remote_users} = - :timer.tc(fn -> - Repo.all( - from(u in User, - where: u.id != ^user.id, - where: u.local == false, - order_by: fragment("RANDOM()"), - limit: 10 - ) - ) - end) - - IO.puts("Fetching remote users take #{to_sec(time)} sec.\n") - - generate_activities(user, users) - - generate_remote_activities(user, remote_users) - - generate_dms(user, users, opts) - - {:ok, activity} = generate_long_thread(user, users, opts) - - generate_non_visible_message(user, users) - - IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}") - - IO.puts("Activities in DB: #{Repo.aggregate(from(a in Pleroma.Activity), :count, :id)}") - - IO.puts("Objects in DB: #{Repo.aggregate(from(o in Pleroma.Object), :count, :id)}") - - IO.puts( - "Notifications in DB: #{Repo.aggregate(from(n in Pleroma.Notification), :count, :id)}" - ) - - fetch_user(user) - query_timelines(user) - query_notifications(user) - query_dms(user) - query_long_thread(user, activity) - Pleroma.Config.put([:instance, :skip_thread_containment], false) - query_timelines(user) - end - - defp clean_tables do - IO.puts("Deleting old data...\n") - Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;") - Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;") - Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;") - end -end -- cgit v1.2.3 From ad42837244ba4c945b76c5addaffe47353cf62a8 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Wed, 9 Oct 2019 17:03:54 +0300 Subject: Ability to toggle activation status and permission group for a group of users --- lib/pleroma/moderation_log.ex | 60 +++++++-------- lib/pleroma/user.ex | 16 +++- lib/pleroma/web/admin_api/admin_api_controller.ex | 89 ++++++++++------------- lib/pleroma/web/admin_api/views/account_view.ex | 6 ++ lib/pleroma/web/router.ex | 14 +--- 5 files changed, 95 insertions(+), 90 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 352cad433..42649ff02 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -86,18 +86,18 @@ defmodule Pleroma.ModerationLog do parsed_datetime end - @spec insert_log(%{actor: User, subject: User, action: String.t(), permission: String.t()}) :: + @spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, - subject: %User{} = subject, + subject: subjects, action: action, permission: permission }) do %ModerationLog{ data: %{ "actor" => user_to_map(actor), - "subject" => user_to_map(subject), + "subject" => user_to_map(subjects), "action" => action, "permission" => permission, "message" => "" @@ -303,13 +303,16 @@ defmodule Pleroma.ModerationLog do end @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any} - defp insert_log_entry_with_message(entry) do entry.data["message"] |> put_in(get_log_entry_message(entry)) |> Repo.insert() end + defp user_to_map(users) when is_list(users) do + users |> Enum.map(&user_to_map/1) + end + defp user_to_map(%User{} = user) do user |> Map.from_struct() @@ -363,12 +366,7 @@ defmodule Pleroma.ModerationLog do "subjects" => subjects } }) do - nicknames = - subjects - |> Enum.map(&"@#{&1["nickname"]}") - |> Enum.join(", ") - - "@#{actor_nickname} created users: #{nicknames}" + "@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -376,10 +374,10 @@ defmodule Pleroma.ModerationLog do data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "activate", - "subject" => %{"nickname" => subject_nickname, "type" => "user"} + "subject" => users } }) do - "@#{actor_nickname} activated user @#{subject_nickname}" + "@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -387,10 +385,10 @@ defmodule Pleroma.ModerationLog do data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "deactivate", - "subject" => %{"nickname" => subject_nickname, "type" => "user"} + "subject" => users } }) do - "@#{actor_nickname} deactivated user @#{subject_nickname}" + "@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -402,14 +400,9 @@ defmodule Pleroma.ModerationLog do "action" => "tag" } }) do - nicknames_string = - nicknames - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - tags_string = tags |> Enum.join(", ") - "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}" + "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -421,14 +414,9 @@ defmodule Pleroma.ModerationLog do "action" => "untag" } }) do - nicknames_string = - nicknames - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - tags_string = tags |> Enum.join(", ") - "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}" + "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -436,11 +424,11 @@ defmodule Pleroma.ModerationLog do data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "grant", - "subject" => %{"nickname" => subject_nickname}, + "subject" => users, "permission" => permission } }) do - "@#{actor_nickname} made @#{subject_nickname} #{permission}" + "@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -448,11 +436,11 @@ defmodule Pleroma.ModerationLog do data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "revoke", - "subject" => %{"nickname" => subject_nickname}, + "subject" => users, "permission" => permission } }) do - "@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}" + "@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}" end @spec get_log_entry_message(ModerationLog) :: String.t() @@ -551,4 +539,16 @@ defmodule Pleroma.ModerationLog do }) do "@#{actor_nickname} deleted status ##{subject_id}" end + + defp nicknames_to_string(nicknames) do + nicknames + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + end + + defp users_to_nicknames_string(users) do + users + |> Enum.map(&"@#{&1["nickname"]}") + |> Enum.join(", ") + end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2cfb13a8c..a76a5ad70 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1059,7 +1059,15 @@ defmodule Pleroma.User do BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status}) end - def deactivate(%User{} = user, status \\ true) do + def deactivate(user, status \\ true) + + def deactivate(users, status) when is_list(users) do + Repo.transaction(fn -> + for user <- users, do: deactivate(user, status) + end) + end + + def deactivate(%User{} = user, status) do with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do Enum.each(get_followers(user), &invalidate_cache/1) Enum.each(get_friends(user), &update_follower_count/1) @@ -1625,6 +1633,12 @@ defmodule Pleroma.User do `fun` is called with the `user.info`. """ + def update_info(users, fun) when is_list(users) do + Repo.transaction(fn -> + for user <- users, do: update_info(user, fun) + end) + end + def update_info(user, fun) do user |> change_info(fun) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 513bae800..d825a5d28 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -231,22 +231,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do - user = User.get_cached_by_nickname(nickname) + def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.deactivate(users, false) - {:ok, updated_user} = User.deactivate(user, !user.info.deactivated) + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "activate" + }) - action = if user.info.deactivated, do: "activate", else: "deactivate" + conn + |> put_view(AccountView) + |> render("index.json", %{users: Keyword.values(updated_users)}) + end + + def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.deactivate(users, true) ModerationLog.insert_log(%{ actor: admin, - subject: user, - action: action + subject: users, + action: "deactivate" }) conn |> put_view(AccountView) - |> render("show.json", %{user: updated_user}) + |> render("index.json", %{users: Keyword.values(updated_users)}) end def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do @@ -315,20 +327,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def right_add(%{assigns: %{user: admin}} = conn, %{ "permission_group" => permission_group, - "nickname" => nickname + "nicknames" => nicknames }) when permission_group in ["moderator", "admin"] do info = Map.put(%{}, "is_" <> permission_group, true) - {:ok, user} = - nickname - |> User.get_cached_by_nickname() - |> User.update_info(&User.Info.admin_api_update(&1, info)) + users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) + + User.update_info(users, &User.Info.admin_api_update(&1, info)) ModerationLog.insert_log(%{ action: "grant", actor: admin, - subject: user, + subject: users, permission: permission_group }) @@ -349,58 +360,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do }) end - def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do - render_error(conn, :forbidden, "You can't revoke your own admin status.") - end - def right_delete( - %{assigns: %{user: admin}} = conn, + %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn, %{ "permission_group" => permission_group, - "nickname" => nickname + "nicknames" => nicknames } ) when permission_group in ["moderator", "admin"] do - info = Map.put(%{}, "is_" <> permission_group, false) + with false <- Enum.member?(nicknames, admin_nickname) do + info = Map.put(%{}, "is_" <> permission_group, false) - {:ok, user} = - nickname - |> User.get_cached_by_nickname() - |> User.update_info(&User.Info.admin_api_update(&1, info)) + users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) - ModerationLog.insert_log(%{ - action: "revoke", - actor: admin, - subject: user, - permission: permission_group - }) - - json(conn, info) - end - - def right_delete(conn, _) do - render_error(conn, :not_found, "No such permission_group") - end - - def set_activation_status(%{assigns: %{user: admin}} = conn, %{ - "nickname" => nickname, - "status" => status - }) do - with {:ok, status} <- Ecto.Type.cast(:boolean, status), - %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, _} <- User.deactivate(user, !status) do - action = if(user.info.deactivated, do: "activate", else: "deactivate") + User.update_info(users, &User.Info.admin_api_update(&1, info)) ModerationLog.insert_log(%{ + action: "revoke", actor: admin, - subject: user, - action: action + subject: users, + permission: permission_group }) - json_response(conn, :no_content, "") + json(conn, info) + else + _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.") end end + def right_delete(conn, _) do + render_error(conn, :not_found, "No such permission_group") + end + def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do with {:ok, _message} <- Relay.follow(target) do ModerationLog.insert_log(%{ diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index a96affd40..441269162 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -19,6 +19,12 @@ defmodule Pleroma.Web.AdminAPI.AccountView do } end + def render("index.json", %{users: users}) do + %{ + users: render_many(users, AccountView, "show.json", as: :user) + } + end + def render("show.json", %{user: user}) do avatar = User.avatar_url(user) |> MediaProxy.url() display_name = HTML.strip_tags(user.name || user.nickname) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ae799b8ac..894375357 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -136,21 +136,15 @@ defmodule Pleroma.Web.Router do delete("/users", AdminAPIController, :user_delete) post("/users", AdminAPIController, :users_create) - patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) + patch("/users/activate", AdminAPIController, :user_activate) + patch("/users/deactivate", AdminAPIController, :user_deactivate) put("/users/tag", AdminAPIController, :tag_users) delete("/users/tag", AdminAPIController, :untag_users) get("/users/:nickname/permission_group", AdminAPIController, :right_get) get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get) - post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add) - - delete( - "/users/:nickname/permission_group/:permission_group", - AdminAPIController, - :right_delete - ) - - put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status) + post("/users/permission_group/:permission_group", AdminAPIController, :right_add) + delete("/users/permission_group/:permission_group", AdminAPIController, :right_delete) post("/relay", AdminAPIController, :relay_follow) delete("/relay", AdminAPIController, :relay_unfollow) -- cgit v1.2.3 From f5104f36bbec7d49d4ff5acee4b9d28223c6474d Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Fri, 11 Oct 2019 00:24:31 +0300 Subject: Deprecate /api/pleroma/admin/users/:nickname/toggle_activation instead of deleting it --- lib/pleroma/web/admin_api/admin_api_controller.ex | 20 ++++++++++++++++++++ lib/pleroma/web/router.ex | 1 + 2 files changed, 21 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index d825a5d28..5b513bd7c 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -46,6 +46,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do :user_delete, :users_create, :user_toggle_activation, + :user_activate, + :user_deactivate, :tag_users, :untag_users, :right_add, @@ -231,6 +233,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + user = User.get_cached_by_nickname(nickname) + + {:ok, updated_user} = User.deactivate(user, !user.info.deactivated) + + action = if user.info.deactivated, do: "activate", else: "deactivate" + + ModerationLog.insert_log(%{ + actor: admin, + subject: [user], + action: action + }) + + conn + |> put_view(AccountView) + |> render("show.json", %{user: updated_user}) + end + def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.deactivate(users, false) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 894375357..a79df51a2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -136,6 +136,7 @@ defmodule Pleroma.Web.Router do delete("/users", AdminAPIController, :user_delete) post("/users", AdminAPIController, :users_create) + patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) patch("/users/activate", AdminAPIController, :user_activate) patch("/users/deactivate", AdminAPIController, :user_deactivate) put("/users/tag", AdminAPIController, :tag_users) -- cgit v1.2.3 From aaa4252f416fbad099f95232de4cf6eab11dd7d2 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Fri, 11 Oct 2019 15:58:45 +0300 Subject: Deprecate POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group instead of deleting it --- lib/pleroma/web/admin_api/admin_api_controller.ex | 61 +++++++++++++++++++++-- lib/pleroma/web/router.ex | 18 ++++++- 2 files changed, 74 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 5b513bd7c..33e2180ec 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -345,7 +345,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> Enum.into(%{}, &{&1, true}) end - def right_add(%{assigns: %{user: admin}} = conn, %{ + def right_add_multiple(%{assigns: %{user: admin}} = conn, %{ "permission_group" => permission_group, "nicknames" => nicknames }) @@ -366,6 +366,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do json(conn, info) end + def right_add_multiple(conn, _) do + render_error(conn, :not_found, "No such permission_group") + end + + def right_add(%{assigns: %{user: admin}} = conn, %{ + "permission_group" => permission_group, + "nickname" => nickname + }) + when permission_group in ["moderator", "admin"] do + info = Map.put(%{}, "is_" <> permission_group, true) + + {:ok, user} = + nickname + |> User.get_cached_by_nickname() + |> User.update_info(&User.Info.admin_api_update(&1, info)) + + ModerationLog.insert_log(%{ + action: "grant", + actor: admin, + subject: [user], + permission: permission_group + }) + + json(conn, info) + end + def right_add(conn, _) do render_error(conn, :not_found, "No such permission_group") end @@ -380,7 +406,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do }) end - def right_delete( + def right_delete_multiple( %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn, %{ "permission_group" => permission_group, @@ -408,10 +434,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def right_delete(conn, _) do + def right_delete_multiple(conn, _) do render_error(conn, :not_found, "No such permission_group") end + def right_delete( + %{assigns: %{user: admin}} = conn, + %{ + "permission_group" => permission_group, + "nickname" => nickname + } + ) + when permission_group in ["moderator", "admin"] do + info = Map.put(%{}, "is_" <> permission_group, false) + + {:ok, user} = + nickname + |> User.get_cached_by_nickname() + |> User.update_info(&User.Info.admin_api_update(&1, info)) + + ModerationLog.insert_log(%{ + action: "revoke", + actor: admin, + subject: [user], + permission: permission_group + }) + + json(conn, info) + end + + def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do + render_error(conn, :forbidden, "You can't revoke your own admin status.") + end + def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do with {:ok, _message} <- Relay.follow(target) do ModerationLog.insert_log(%{ diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index a79df51a2..80651f3ff 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -144,8 +144,22 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/permission_group", AdminAPIController, :right_get) get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get) - post("/users/permission_group/:permission_group", AdminAPIController, :right_add) - delete("/users/permission_group/:permission_group", AdminAPIController, :right_delete) + + post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add) + + delete( + "/users/:nickname/permission_group/:permission_group", + AdminAPIController, + :right_delete + ) + + post("/users/permission_group/:permission_group", AdminAPIController, :right_add_multiple) + + delete( + "/users/permission_group/:permission_group", + AdminAPIController, + :right_delete_multiple + ) post("/relay", AdminAPIController, :relay_follow) delete("/relay", AdminAPIController, :relay_unfollow) -- cgit v1.2.3 From cc6875b582df49d2cb780e0940b85d5b04fe0e74 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Fri, 11 Oct 2019 19:12:29 +0300 Subject: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays --- lib/mix/tasks/pleroma/relay.ex | 10 +++------- lib/pleroma/web/activity_pub/relay.ex | 14 ++++++++++++++ lib/pleroma/web/admin_api/admin_api_controller.ex | 10 ++++++++++ lib/pleroma/web/router.ex | 1 + 4 files changed, 28 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index d7a7b599f..7ef5f9678 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -5,7 +5,6 @@ defmodule Mix.Tasks.Pleroma.Relay do use Mix.Task import Mix.Pleroma - alias Pleroma.User alias Pleroma.Web.ActivityPub.Relay @shortdoc "Manages remote relays" @@ -36,13 +35,10 @@ defmodule Mix.Tasks.Pleroma.Relay do def run(["list"]) do start_pleroma() - with %User{following: following} = _user <- Relay.get_actor() do - following - |> Enum.map(fn entry -> URI.parse(entry).host end) - |> Enum.uniq() - |> Enum.each(&shell_info(&1)) + with {:ok, list} <- Relay.list() do + list |> Enum.each(&shell_info(&1)) else - e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}") + {:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}") end end end diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index c2ac38907..03fc434a9 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -51,6 +51,20 @@ defmodule Pleroma.Web.ActivityPub.Relay do def publish(_), do: {:error, "Not implemented"} + @spec list() :: {:ok, [String.t()]} | {:error, any()} + def list do + with %User{following: following} = _user <- get_actor() do + list = + following + |> Enum.map(fn entry -> URI.parse(entry).host end) + |> Enum.uniq() + + {:ok, list} + else + error -> format_error(error) + end + end + defp format_error({:error, error}), do: format_error(error) defp format_error(error) do diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 513bae800..24dda75a9 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -401,6 +401,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def relay_list(conn, _params) do + with {:ok, list} <- Relay.list() do + json(conn, %{relays: list}) + else + _ -> + conn + |> put_status(500) + end + end + def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do with {:ok, _message} <- Relay.follow(target) do ModerationLog.insert_log(%{ diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ae799b8ac..8cc967af9 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -152,6 +152,7 @@ defmodule Pleroma.Web.Router do put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status) + get("/relay", AdminAPIController, :relay_list) post("/relay", AdminAPIController, :relay_follow) delete("/relay", AdminAPIController, :relay_unfollow) -- cgit v1.2.3 From fbe5a00475331962d7112ed5a0d5cf8bb5bf3ece Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Mon, 30 Sep 2019 16:04:30 +0300 Subject: For pleroma.emoji downloaded packs, generate pack.json instead --- lib/mix/tasks/pleroma/emoji.ex | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 881a6f725..1470b9b72 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -158,19 +158,21 @@ defmodule Mix.Tasks.Pleroma.Emoji do file_list: files_to_unzip ) - IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name])) - - emoji_txt_str = - Enum.map( - files, - fn {shortcode, path} -> - emojo_path = Path.join("/emoji/#{pack_name}", path) - "#{shortcode}, #{emojo_path}" - end - ) - |> Enum.join("\n") - - File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str) + IO.puts(IO.ANSI.format(["Writing pack.json for ", :bright, pack_name])) + + pack_json = %{ + pack: %{ + "license" => pack["license"], + "homepage" => pack["homepage"], + "description" => pack["description"], + "fallback-src" => pack["src"], + "fallback-src-sha256" => pack["src_sha256"], + "share-files" => true + }, + files: files + } + + File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(pack_json, pretty: true)) else IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"])) end -- cgit v1.2.3 From 5bd0717de286a918e20c5ef2409e5f4cbcea8524 Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 15 Oct 2019 19:10:22 +0900 Subject: Add `Sec-WebSocket-Protocol` to response header --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 3c26eb406..1a757363f 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -35,6 +35,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do {_, stream} <- List.keyfind(params, "stream", 0), {:ok, user} <- allow_request(stream, [access_token, sec_websocket]), topic when is_binary(topic) <- expand_topic(stream, params) do + req = + if sec_websocket != nil do + :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req) + else + req + end + {:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}} else {:error, code} -> -- cgit v1.2.3 From e7bb762ec256bb5dc607797c042aa9432a9aa993 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 15 Oct 2019 15:16:17 +0300 Subject: don't stream in benchmark env --- lib/pleroma/web/streamer/streamer.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex index 8cf719277..2fc7ac8cf 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer/streamer.ex @@ -49,7 +49,7 @@ defmodule Pleroma.Web.Streamer do end end - defp handle_should_send(_) do - true - end + defp handle_should_send(:benchmark), do: false + + defp handle_should_send(_), do: true end -- cgit v1.2.3 From c10ce113d487d71c4daa6fabcc641a5caa0d04cb Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 16 Oct 2019 12:52:47 +0300 Subject: User search: Remove trigram and refactor the module - Remove trigram as it tends to rank garbage results highly, resulting in it prioritized above fts, which gives actually decent results. ACKed by kaniini and lain on irc. - Remove a test for handling misspelled requests, since we no longer have trigram - Remove a test for searching users with `nil` display names, because it is unrealistic, we don't accept usernames that are not >1 char strings - Make rank boosting for followers/followees sane again, previous values resulted in garbage matches getting on top just because the users are followers/followees --- lib/pleroma/user/search.ex | 152 +++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 102 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index 6fb2c2352..fb2f3fedb 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -4,7 +4,6 @@ defmodule Pleroma.User.Search do alias Pleroma.Pagination - alias Pleroma.Repo alias Pleroma.User import Ecto.Query @@ -23,18 +22,10 @@ defmodule Pleroma.User.Search do maybe_resolve(resolve, for_user, query_string) - {:ok, results} = - Repo.transaction(fn -> - Ecto.Adapters.SQL.query( - Repo, - "select set_limit(#{@similarity_threshold})", - [] - ) - - query_string - |> search_query(for_user, following) - |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) - end) + results = + query_string + |> search_query(for_user, following) + |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) results end @@ -56,15 +47,52 @@ defmodule Pleroma.User.Search do |> base_query(following) |> filter_blocked_user(for_user) |> filter_blocked_domains(for_user) - |> search_subqueries(query_string) - |> union_subqueries - |> distinct_query() - |> boost_search_rank_query(for_user) + |> fts_subquery(query_string) |> subquery() + |> where([u], u.search_rank > @similarity_threshold) + |> boost_search_rank(for_user) |> order_by(desc: :search_rank) |> maybe_restrict_local(for_user) end + @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/ + defp fts_subquery(query, query_string) do + {nickname_weight, name_weight} = + if String.match?(query_string, @nickname_regex) do + {"A", "B"} + else + {"B", "A"} + end + + query_string = to_tsquery(query_string) + + from( + u in query, + select_merge: %{ + search_rank: + fragment( + """ + ts_rank_cd((setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)), to_tsquery('simple', ?)) + """, + u.name, + ^name_weight, + u.nickname, + ^nickname_weight, + ^query_string + ) + } + ) + end + + defp to_tsquery(query_string) do + String.trim_trailing(query_string, "@" <> local_domain()) + |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ") + |> String.trim() + |> String.split() + |> Enum.map(&(&1 <> ":*")) + |> Enum.join(" | ") + end + defp base_query(_user, false), do: User defp base_query(user, true), do: User.get_followers_query(user) @@ -87,21 +115,6 @@ defmodule Pleroma.User.Search do defp filter_blocked_domains(query, _), do: query - defp union_subqueries({fts_subquery, trigram_subquery}) do - from(s in trigram_subquery, union_all: ^fts_subquery) - end - - defp search_subqueries(base_query, query_string) do - { - fts_search_subquery(base_query, query_string), - trigram_search_subquery(base_query, query_string) - } - end - - defp distinct_query(q) do - from(s in subquery(q), order_by: s.search_type, distinct: s.id) - end - defp maybe_resolve(true, user, query) do case {limit(), user} do {:all, _} -> :noop @@ -126,9 +139,9 @@ defmodule Pleroma.User.Search do defp restrict_local(q), do: where(q, [u], u.local == true) - defp boost_search_rank_query(query, nil), do: query + defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) - defp boost_search_rank_query(query, for_user) do + defp boost_search_rank(query, %User{} = for_user) do friends_ids = User.get_friends_ids(for_user) followers_ids = User.get_followers_ids(for_user) @@ -137,8 +150,8 @@ defmodule Pleroma.User.Search do search_rank: fragment( """ - CASE WHEN (?) THEN 0.5 + (?) * 1.3 - WHEN (?) THEN 0.5 + (?) * 1.2 + CASE WHEN (?) THEN (?) * 1.5 + WHEN (?) THEN (?) * 1.3 WHEN (?) THEN (?) * 1.1 ELSE (?) END """, @@ -154,70 +167,5 @@ defmodule Pleroma.User.Search do ) end - @spec fts_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t() - defp fts_search_subquery(query, term) do - processed_query = - String.trim_trailing(term, "@" <> local_domain()) - |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ") - |> 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 - - @spec trigram_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t() - defp trigram_search_subquery(query, term) do - term = String.trim_trailing(term, "@" <> local_domain()) - - from( - u in query, - 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 - - defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) + defp boost_search_rank(query, _for_user), do: query end -- cgit v1.2.3 From 0a5175ecbb796cf3c192a42dc561debd73640a54 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 16 Oct 2019 13:49:33 +0300 Subject: Order fts results by trigram --- lib/pleroma/user/search.ex | 48 +++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index fb2f3fedb..0d697fe3d 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -7,7 +7,6 @@ defmodule Pleroma.User.Search do alias Pleroma.User import Ecto.Query - @similarity_threshold 0.25 @limit 20 def search(query_string, opts \\ []) do @@ -47,16 +46,16 @@ defmodule Pleroma.User.Search do |> base_query(following) |> filter_blocked_user(for_user) |> filter_blocked_domains(for_user) - |> fts_subquery(query_string) - |> subquery() - |> where([u], u.search_rank > @similarity_threshold) + |> fts_search(query_string) + |> trigram_rank(query_string) |> boost_search_rank(for_user) + |> subquery() |> order_by(desc: :search_rank) |> maybe_restrict_local(for_user) end @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/ - defp fts_subquery(query, query_string) do + defp fts_search(query, query_string) do {nickname_weight, name_weight} = if String.match?(query_string, @nickname_regex) do {"A", "B"} @@ -68,19 +67,17 @@ defmodule Pleroma.User.Search do from( u in query, - select_merge: %{ - search_rank: - fragment( - """ - ts_rank_cd((setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)), to_tsquery('simple', ?)) - """, - u.name, - ^name_weight, - u.nickname, - ^nickname_weight, - ^query_string - ) - } + where: + fragment( + """ + (setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)) @@ to_tsquery('simple', ?) + """, + u.name, + ^name_weight, + u.nickname, + ^nickname_weight, + ^query_string + ) ) end @@ -93,6 +90,21 @@ defmodule Pleroma.User.Search do |> Enum.join(" | ") end + defp trigram_rank(query, query_string) do + from( + u in query, + select_merge: %{ + search_rank: + fragment( + "similarity(?, trim(? || ' ' || coalesce(?, '')))", + ^query_string, + u.nickname, + u.name + ) + } + ) + end + defp base_query(_user, false), do: User defp base_query(user, true), do: User.get_followers_query(user) -- cgit v1.2.3 From 10ff01acd95d42314b4eb923e5b7a7191356b73e Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 16 Oct 2019 21:59:21 +0300 Subject: [#1304] Moved all non-mutes / non-blocks fields from User.Info to User. WIP. --- lib/mix/tasks/pleroma/user.ex | 18 +- lib/pleroma/daemons/digest_email_daemon.ex | 2 +- lib/pleroma/emails/user_email.ex | 2 +- lib/pleroma/formatter.ex | 2 +- lib/pleroma/notification.ex | 10 +- .../plugs/admin_secret_authentication_plug.ex | 2 +- lib/pleroma/plugs/oauth_plug.ex | 2 +- lib/pleroma/plugs/user_enabled_plug.ex | 2 +- lib/pleroma/plugs/user_is_admin_plug.ex | 2 +- lib/pleroma/stats.ex | 7 +- lib/pleroma/user.ex | 554 ++++++++++++++++----- lib/pleroma/user/info.ex | 371 -------------- lib/pleroma/user/query.ex | 21 +- lib/pleroma/web/activity_pub/activity_pub.ex | 20 +- .../web/activity_pub/activity_pub_controller.ex | 6 +- .../web/activity_pub/mrf/anti_link_spam_policy.ex | 2 +- lib/pleroma/web/activity_pub/publisher.ex | 8 +- lib/pleroma/web/activity_pub/transmogrifier.ex | 12 +- lib/pleroma/web/activity_pub/views/user_view.ex | 28 +- lib/pleroma/web/admin_api/admin_api_controller.ex | 22 +- lib/pleroma/web/admin_api/views/account_view.ex | 5 +- lib/pleroma/web/common_api/common_api.ex | 12 +- lib/pleroma/web/masto_fe_controller.ex | 2 +- .../mastodon_api/controllers/account_controller.ex | 64 ++- lib/pleroma/web/mastodon_api/views/account_view.ex | 68 ++- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- lib/pleroma/web/oauth/oauth_controller.ex | 4 +- lib/pleroma/web/ostatus/handlers/follow_handler.ex | 2 +- lib/pleroma/web/ostatus/ostatus.ex | 9 +- .../pleroma_api/controllers/account_controller.ex | 16 +- .../pleroma_api/controllers/mascot_controller.ex | 4 +- lib/pleroma/web/salmon/salmon.ex | 8 +- lib/pleroma/web/streamer/worker.ex | 2 +- .../web/twitter_api/twitter_api_controller.ex | 9 +- lib/pleroma/web/views/masto_fe_view.ex | 8 +- lib/pleroma/web/websub/websub.ex | 4 +- 36 files changed, 610 insertions(+), 702 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 134b5bccc..265c5f1a7 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -109,10 +109,10 @@ defmodule Mix.Tasks.Pleroma.User do start_pleroma() with %User{} = user <- User.get_cached_by_nickname(nickname) do - {:ok, user} = User.deactivate(user, !user.info.deactivated) + {:ok, user} = User.deactivate(user, !user.deactivated) shell_info( - "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated" + "Activation status of #{nickname}: #{if(user.deactivated, do: "de", else: "")}activated" ) else _ -> @@ -340,7 +340,7 @@ defmodule Mix.Tasks.Pleroma.User do with %User{} = user <- User.get_cached_by_nickname(nickname) do {:ok, user} = User.toggle_confirmation(user) - message = if user.info.confirmation_pending, do: "needs", else: "doesn't need" + message = if user.confirmation_pending, do: "needs", else: "doesn't need" shell_info("#{nickname} #{message} confirmation.") else @@ -364,23 +364,23 @@ defmodule Mix.Tasks.Pleroma.User do end defp set_moderator(user, value) do - {:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_moderator: value})) + {:ok, user} = User.update_and_set_cache(user, %{is_moderator: value}) - shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}") + shell_info("Moderator status of #{user.nickname}: #{user.is_moderator}") user end defp set_admin(user, value) do - {:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_admin: value})) + {:ok, user} = User.update_and_set_cache(user, %{is_admin: value}) - shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}") + shell_info("Admin status of #{user.nickname}: #{user.is_admin}") user end defp set_locked(user, value) do - {:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: value})) + {:ok, user} = User.update_and_set_cache(user, %{locked: value}) - shell_info("Locked status of #{user.nickname}: #{user.info.locked}") + shell_info("Locked status of #{user.nickname}: #{user.locked}") user end end diff --git a/lib/pleroma/daemons/digest_email_daemon.ex b/lib/pleroma/daemons/digest_email_daemon.ex index 462ad2c55..b4c8eaad9 100644 --- a/lib/pleroma/daemons/digest_email_daemon.ex +++ b/lib/pleroma/daemons/digest_email_daemon.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Daemons.DigestEmailDaemon do now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) from(u in inactive_users_query, - where: fragment(~s(? #> '{"email_notifications","digest"}' @> 'true'), u.info), + where: fragment(~s(? ->'digest' @> 'true'), u.email_notifications), where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"), select: u ) diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index 40b67ff56..a10f88f93 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -72,7 +72,7 @@ defmodule Pleroma.Emails.UserEmail do Endpoint, :confirm_email, user.id, - to_string(user.info.confirmation_token) + to_string(user.confirmation_token) ) html_body = """ diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 931b9af2b..19b9af46c 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -127,7 +127,7 @@ defmodule Pleroma.Formatter do end end - defp get_ap_id(%User{info: %{source_data: %{"url" => url}}}) when is_binary(url), do: url + defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url defp get_ap_id(%User{ap_id: ap_id}), do: ap_id defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index d94ae5971..19d22370f 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -40,7 +40,7 @@ defmodule Pleroma.Notification do |> where( [n, a], fragment( - "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')", + "? not in (SELECT ap_id FROM users WHERE deactivated = 'true')", a.actor ) ) @@ -259,7 +259,7 @@ defmodule Pleroma.Notification do def skip?( :followers, activity, - %{info: %{notification_settings: %{"followers" => false}}} = user + %{notification_settings: %{"followers" => false}} = user ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) @@ -269,14 +269,14 @@ defmodule Pleroma.Notification do def skip?( :non_followers, activity, - %{info: %{notification_settings: %{"non_followers" => false}}} = user + %{notification_settings: %{"non_followers" => false}} = user ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) !User.following?(follower, user) end - def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do + def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do actor = activity.data["actor"] followed = User.get_cached_by_ap_id(actor) User.following?(user, followed) @@ -285,7 +285,7 @@ defmodule Pleroma.Notification do def skip?( :non_follows, activity, - %{info: %{notification_settings: %{"non_follows" => false}}} = user + %{notification_settings: %{"non_follows" => false}} = user ) do actor = activity.data["actor"] followed = User.get_cached_by_ap_id(actor) diff --git a/lib/pleroma/plugs/admin_secret_authentication_plug.ex b/lib/pleroma/plugs/admin_secret_authentication_plug.ex index 5baf8a691..fdadd476e 100644 --- a/lib/pleroma/plugs/admin_secret_authentication_plug.ex +++ b/lib/pleroma/plugs/admin_secret_authentication_plug.ex @@ -19,7 +19,7 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do def call(%{params: %{"admin_token" => admin_token}} = conn, _) do if secret_token() && admin_token == secret_token() do conn - |> assign(:user, %User{info: %{is_admin: true}}) + |> assign(:user, %User{is_admin: true}) else conn end diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 86bc4aa3a..fd004fcd2 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -71,7 +71,7 @@ defmodule Pleroma.Plugs.OAuthPlug do ) # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength - with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do + with %Token{user: %{deactivated: false} = user} = token_record <- Repo.one(query) do {:ok, user, token_record} end end diff --git a/lib/pleroma/plugs/user_enabled_plug.ex b/lib/pleroma/plugs/user_enabled_plug.ex index da892c28b..fbb4bf115 100644 --- a/lib/pleroma/plugs/user_enabled_plug.ex +++ b/lib/pleroma/plugs/user_enabled_plug.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Plugs.UserEnabledPlug do options end - def call(%{assigns: %{user: %User{info: %{deactivated: true}}}} = conn, _) do + def call(%{assigns: %{user: %User{deactivated: true}}} = conn, _) do conn |> assign(:user, nil) end diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex index 4c4b3d610..ee808f31f 100644 --- a/lib/pleroma/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/plugs/user_is_admin_plug.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do options end - def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do + def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do conn end diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index df80fbaa4..8154a09b7 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -68,12 +68,7 @@ defmodule Pleroma.Stats do domain_count = Enum.count(peers) - status_query = - from(u in User.Query.build(%{local: true}), - select: fragment("sum((?->>'note_count')::int)", u.info) - ) - - status_count = Repo.one(status_query) + status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count) user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2cfb13a8c..165342011 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -63,6 +63,62 @@ defmodule Pleroma.User do field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime_usec) field(:last_digest_emailed_at, :naive_datetime) + + field(:banner, :map, default: %{}) + field(:background, :map, default: %{}) + field(:source_data, :map, default: %{}) + field(:note_count, :integer, default: 0) + field(:follower_count, :integer, default: 0) + # Should be filled in only for remote users + field(:following_count, :integer, default: nil) + field(:locked, :boolean, default: false) + field(:confirmation_pending, :boolean, default: false) + field(:password_reset_pending, :boolean, default: false) + field(:confirmation_token, :string, default: nil) + field(:default_scope, :string, default: "public") + field(:blocks, {:array, :string}, default: []) + field(:domain_blocks, {:array, :string}, default: []) + field(:mutes, {:array, :string}, default: []) + field(:muted_reblogs, {:array, :string}, default: []) + field(:muted_notifications, {:array, :string}, default: []) + field(:subscribers, {:array, :string}, default: []) + field(:deactivated, :boolean, default: false) + field(:no_rich_text, :boolean, default: false) + field(:ap_enabled, :boolean, default: false) + field(:is_moderator, :boolean, default: false) + field(:is_admin, :boolean, default: false) + field(:show_role, :boolean, default: true) + field(:settings, :map, default: nil) + field(:magic_key, :string, default: nil) + field(:uri, :string, default: nil) + field(:topic, :string, default: nil) + field(:hub, :string, default: nil) + field(:salmon, :string, default: nil) + field(:hide_followers_count, :boolean, default: false) + field(:hide_follows_count, :boolean, default: false) + field(:hide_followers, :boolean, default: false) + field(:hide_follows, :boolean, default: false) + field(:hide_favorites, :boolean, default: true) + field(:unread_conversation_count, :integer, default: 0) + field(:pinned_activities, {:array, :string}, default: []) + field(:email_notifications, :map, default: %{"digest" => false}) + field(:mascot, :map, default: nil) + field(:emoji, {:array, :map}, default: []) + field(:pleroma_settings_store, :map, default: %{}) + field(:fields, {:array, :map}, default: nil) + field(:raw_fields, {:array, :map}, default: []) + field(:discoverable, :boolean, default: false) + field(:skip_thread_containment, :boolean, default: false) + + field(:notification_settings, :map, + default: %{ + "followers" => true, + "follows" => true, + "non_follows" => true, + "non_followers" => true + } + ) + has_many(:notifications, Notification) has_many(:registrations, Registration) has_many(:deliveries, Delivery) @@ -71,7 +127,7 @@ defmodule Pleroma.User do timestamps() end - def auth_active?(%User{info: %User.Info{confirmation_pending: true}}), + def auth_active?(%User{confirmation_pending: true}), do: !Pleroma.Config.get([:instance, :account_activation_required]) def auth_active?(%User{}), do: true @@ -86,8 +142,8 @@ defmodule Pleroma.User do def visible_for?(_, _), do: false - def superuser?(%User{local: true, info: %User.Info{is_admin: true}}), do: true - def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true + def superuser?(%User{local: true, is_admin: true}), do: true + def superuser?(%User{local: true, is_moderator: true}), do: true def superuser?(_), do: false def avatar_url(user, options \\ []) do @@ -98,13 +154,13 @@ defmodule Pleroma.User do end def banner_url(user, options \\ []) do - case user.info.banner do + case user.banner do %{"url" => [%{"href" => href} | _]} -> href _ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png" end end - def profile_url(%User{info: %{source_data: %{"url" => url}}}), do: url + def profile_url(%User{source_data: %{"url" => url}}), do: url def profile_url(%User{ap_id: ap_id}), do: ap_id def profile_url(_), do: nil @@ -119,15 +175,15 @@ defmodule Pleroma.User do def user_info(%User{} = user, args \\ %{}) do following_count = - Map.get(args, :following_count, user.info.following_count || following_count(user)) + Map.get(args, :following_count, user.following_count || following_count(user)) - follower_count = Map.get(args, :follower_count, user.info.follower_count) + follower_count = Map.get(args, :follower_count, user.follower_count) %{ - note_count: user.info.note_count, - locked: user.info.locked, - confirmation_pending: user.info.confirmation_pending, - default_scope: user.info.default_scope + note_count: user.note_count, + locked: user.locked, + confirmation_pending: user.confirmation_pending, + default_scope: user.default_scope } |> Map.put(:following_count, following_count) |> Map.put(:follower_count, follower_count) @@ -157,9 +213,7 @@ defmodule Pleroma.User do @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t() def restrict_deactivated(query) do - from(u in query, - where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info) - ) + from(u in query, where: u.deactivated != ^true) end def following_count(%User{following: []}), do: 0 @@ -170,6 +224,64 @@ defmodule Pleroma.User do |> Repo.aggregate(:count, :id) end + @info_fields [ + :banner, + :background, + :source_data, + :note_count, + :follower_count, + :following_count, + :locked, + :confirmation_pending, + :password_reset_pending, + :confirmation_token, + :default_scope, + :blocks, + :domain_blocks, + :mutes, + :muted_reblogs, + :muted_notifications, + :subscribers, + :deactivated, + :no_rich_text, + :ap_enabled, + :is_moderator, + :is_admin, + :show_role, + :settings, + :magic_key, + :uri, + :topic, + :hub, + :salmon, + :hide_followers_count, + :hide_follows_count, + :hide_followers, + :hide_follows, + :hide_favorites, + :unread_conversation_count, + :pinned_activities, + :email_notifications, + :mascot, + :emoji, + :pleroma_settings_store, + :fields, + :raw_fields, + :discoverable, + :skip_thread_containment, + :notification_settings + ] + + def info_fields, do: @info_fields + + defp truncate_fields_param(params) do + if Map.has_key?(params, :fields) do + Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1)) + else + params + end + end + defp truncate_if_exists(params, key, max_length) do if Map.has_key?(params, key) and is_binary(params[key]) do {value, _chopped} = String.split_at(params[key], max_length) @@ -188,18 +300,20 @@ defmodule Pleroma.User do |> Map.put(:info, params[:info] || %{}) |> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:bio, bio_limit) + |> truncate_fields_param() changeset = %User{local: false} - |> cast(params, [:bio, :name, :ap_id, :nickname, :avatar]) + |> cast(params, [:bio, :name, :ap_id, :nickname, :avatar] ++ @info_fields) |> validate_required([:name, :ap_id]) |> unique_constraint(:nickname) |> validate_format(:nickname, @email_regex) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, max: name_limit) - |> change_info(&User.Info.remote_user_creation(&1, params[:info])) + |> validate_fields(true) + |> change_info(& &1) - case params[:info][:source_data] do + case params[:source_data] do %{"followers" => followers, "following" => following} -> changeset |> put_change(:follower_address, followers) @@ -216,11 +330,12 @@ defmodule Pleroma.User do name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) struct - |> cast(params, [:bio, :name, :avatar, :following]) + |> cast(params, [:bio, :name, :avatar, :following] ++ @info_fields) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, min: 1, max: name_limit) + |> validate_fields(false) end def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do @@ -229,20 +344,26 @@ defmodule Pleroma.User do params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now()) + params = if remote?, do: truncate_fields_param(params), else: params + struct - |> cast(params, [ - :bio, - :name, - :follower_address, - :following_address, - :avatar, - :last_refreshed_at - ]) + |> cast( + params, + [ + :bio, + :name, + :follower_address, + :following_address, + :avatar, + :last_refreshed_at + ] ++ @info_fields + ) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, max: name_limit) - |> change_info(&User.Info.user_upgrade(&1, params[:info], remote?)) + |> validate_fields(remote?) + |> change_info(& &1) end def password_update_changeset(struct, params) do @@ -250,8 +371,8 @@ defmodule Pleroma.User do |> cast(params, [:password, :password_confirmation]) |> validate_required([:password, :password_confirmation]) |> validate_confirmation(:password) - |> put_password_hash - |> put_embed(:info, User.Info.set_password_reset_pending(struct.info, false)) + |> put_password_hash() + |> put_change(:password_reset_pending, false) end @spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} @@ -268,19 +389,19 @@ defmodule Pleroma.User do end end + def update_password_reset_pending(user, value) do + user + |> change() + |> put_change(:password_reset_pending, value) + |> update_and_set_cache() + end + def force_password_reset_async(user) do BackgroundWorker.enqueue("force_password_reset", %{"user_id" => user.id}) end @spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} - def force_password_reset(user) do - info_cng = User.Info.set_password_reset_pending(user.info, true) - - user - |> change() - |> put_embed(:info, info_cng) - |> update_and_set_cache() - end + def force_password_reset(user), do: update_password_reset_pending(user, true) def register_changeset(struct, params \\ %{}, opts \\ []) do bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) @@ -294,6 +415,7 @@ defmodule Pleroma.User do end struct + |> confirmation_changeset(need_confirmation: need_confirmation?) |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) @@ -304,7 +426,7 @@ defmodule Pleroma.User do |> validate_format(:email, @email_regex) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, min: 1, max: name_limit) - |> change_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?)) + |> change_info(& &1) |> maybe_validate_required_email(opts[:external]) |> put_password_hash |> put_ap_id() @@ -355,7 +477,7 @@ defmodule Pleroma.User do end def try_send_confirmation_email(%User{} = user) do - if user.info.confirmation_pending && + if user.confirmation_pending && Pleroma.Config.get([:instance, :account_activation_required]) do user |> Pleroma.Emails.UserEmail.account_confirmation_email() @@ -378,7 +500,7 @@ defmodule Pleroma.User do def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} - def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do + def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true}) do {:ok, follower} end @@ -425,13 +547,13 @@ defmodule Pleroma.User do set_cache(follower) end - def follow(%User{} = follower, %User{info: info} = followed) do + def follow(%User{} = follower, %User{} = followed) do deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) ap_followers = followed.follower_address cond do - info.deactivated -> - {:error, "Could not follow user: You are deactivated."} + followed.deactivated -> + {:error, "Could not follow user: #{followed.nickname} is deactivated."} deny_follow_blocked and blocks?(followed, follower) -> {:error, "Could not follow user: #{followed.nickname} blocked you."} @@ -489,7 +611,7 @@ defmodule Pleroma.User do end def locked?(%User{} = user) do - user.info.locked || false + user.locked || false end def get_by_id(id) do @@ -532,6 +654,12 @@ defmodule Pleroma.User do {:ok, user} end + def update_and_set_cache(struct, params) do + struct + |> update_changeset(params) + |> update_and_set_cache() + end + def update_and_set_cache(changeset) do with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do set_cache(user) @@ -721,16 +849,7 @@ defmodule Pleroma.User do def increase_note_count(%User{} = user) do User |> where(id: ^user.id) - |> update([u], - set: [ - info: - fragment( - "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", - u.info, - u.info - ) - ] - ) + |> update([u], inc: [note_count: 1]) |> select([u], u) |> Repo.update_all([]) |> case do @@ -744,12 +863,7 @@ defmodule Pleroma.User do |> where(id: ^user.id) |> update([u], set: [ - info: - fragment( - "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", - u.info, - u.info - ) + note_count: fragment("greatest(0, note_count - 1)") ] ) |> select([u], u) @@ -760,29 +874,17 @@ defmodule Pleroma.User do end end - def update_note_count(%User{} = user) do + def update_note_count(%User{} = user, note_count \\ nil) do note_count = - from( - a in Object, - where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data), - select: count(a.id) - ) - |> Repo.one() - - update_info(user, &User.Info.set_note_count(&1, note_count)) - end - - def update_mascot(user, url) do - info_changeset = - User.Info.mascot_update( - user.info, - url - ) + note_count || + from( + a in Object, + where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data), + select: count(a.id) + ) + |> Repo.one() - user - |> change() - |> put_embed(:info, info_changeset) - |> update_and_set_cache() + update_and_set_cache(user, %{note_count: note_count}) end @spec maybe_fetch_follow_information(User.t()) :: User.t() @@ -799,10 +901,24 @@ defmodule Pleroma.User do def fetch_follow_information(user) do with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do - update_info(user, &User.Info.follow_information_update(&1, info)) + user + |> follow_information_changeset(info) + |> update_and_set_cache() end end + defp follow_information_changeset(user, params) do + user + |> cast(params, [ + :hide_followers, + :hide_follows, + :follower_count, + :following_count, + :hide_followers_count, + :hide_follows_count + ]) + end + def update_follower_count(%User{} = user) do if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do follower_count_query = @@ -813,14 +929,7 @@ defmodule Pleroma.User do |> where(id: ^user.id) |> join(:inner, [u], s in subquery(follower_count_query)) |> update([u, s], - set: [ - info: - fragment( - "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", - u.info, - s.count - ) - ] + set: [follower_count: s.count] ) |> select([u], u) |> Repo.update_all([]) @@ -850,14 +959,7 @@ defmodule Pleroma.User do User |> join(:inner, [u], p in subquery(unread_query)) |> update([u, p], - set: [ - info: - fragment( - "jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)", - u.info, - p.count - ) - ] + set: [unread_conversation_count: p.count] ) |> where([u], u.id == ^user.id) |> select([u], u) @@ -878,14 +980,7 @@ defmodule Pleroma.User do User |> join(:inner, [u], p in subquery(unread_query)) |> update([u, p], - set: [ - info: - fragment( - "jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)", - u.info, - u.info - ) - ] + inc: [unread_conversation_count: 1] ) |> where([u], u.id == ^user.id) |> where([u, p], p.count == 0) @@ -942,14 +1037,14 @@ defmodule Pleroma.User do if blocks?(subscribed, subscriber) and deny_follow_blocked do {:error, "Could not subscribe: #{subscribed.nickname} is blocking you"} else - update_info(subscribed, &User.Info.add_to_subscribers(&1, subscriber.ap_id)) + User.add_to_subscribers(subscribed, subscriber.ap_id) end end end def unsubscribe(unsubscriber, %{ap_id: ap_id}) do with %User{} = user <- get_cached_by_ap_id(ap_id) do - update_info(user, &User.Info.remove_from_subscribers(&1, unsubscriber.ap_id)) + User.remove_from_subscribers(user, unsubscriber.ap_id) end end @@ -1025,7 +1120,7 @@ defmodule Pleroma.User do def subscribed_to?(user, %{ap_id: ap_id}) do with %User{} = target <- get_cached_by_ap_id(ap_id) do - Enum.member?(target.info.subscribers, user.ap_id) + Enum.member?(target.subscribers, user.ap_id) end end @@ -1043,7 +1138,7 @@ defmodule Pleroma.User do @spec subscribers(User.t()) :: [User.t()] def subscribers(user) do - User.Query.build(%{ap_id: user.info.subscribers, deactivated: false}) + User.Query.build(%{ap_id: user.subscribers, deactivated: false}) |> Repo.all() end @@ -1060,7 +1155,7 @@ defmodule Pleroma.User do end def deactivate(%User{} = user, status \\ true) do - with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do + with {:ok, user} <- set_activation_status(user, status) do Enum.each(get_followers(user), &invalidate_cache/1) Enum.each(get_friends(user), &update_follower_count/1) @@ -1068,8 +1163,23 @@ defmodule Pleroma.User do end end - def update_notification_settings(%User{} = user, settings \\ %{}) do - update_info(user, &User.Info.update_notification_settings(&1, settings)) + def update_notification_settings(%User{} = user, settings) do + settings = + settings + |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end) + |> Map.new() + + notification_settings = + user.notification_settings + |> Map.merge(settings) + |> Map.take(["followers", "follows", "non_follows", "non_followers"]) + + params = %{notification_settings: notification_settings} + + user + |> cast(params, [:notification_settings]) + |> validate_required([:notification_settings]) + |> update_and_set_cache() end def delete(%User{} = user) do @@ -1107,7 +1217,7 @@ defmodule Pleroma.User do pages = Pleroma.Config.get!([:fetch_initial_posts, :pages]) # Insert all the posts in reverse order, so they're in the right order on the timeline - user.info.source_data["outbox"] + user.source_data["outbox"] |> Utils.fetch_ordered_collection(pages) |> Enum.reverse() |> Enum.each(&Pleroma.Web.Federator.incoming_ap_doc/1) @@ -1228,7 +1338,7 @@ defmodule Pleroma.User do defp delete_activity(_activity), do: "Doing nothing" - def html_filter_policy(%User{info: %{no_rich_text: true}}) do + def html_filter_policy(%User{no_rich_text: true}) do Pleroma.HTML.Scrubber.TwitterText end @@ -1288,9 +1398,7 @@ defmodule Pleroma.User do end # AP style - def public_key_from_info(%{ - source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}} - }) do + def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do key = public_key_pem |> :public_key.pem_decode() @@ -1301,15 +1409,15 @@ defmodule Pleroma.User do end # OStatus Magic Key - def public_key_from_info(%{magic_key: magic_key}) when not is_nil(magic_key) do + def public_key(%{magic_key: magic_key}) when not is_nil(magic_key) do {:ok, Pleroma.Web.Salmon.decode_key(magic_key)} end - def public_key_from_info(_), do: {:error, "not found key"} + def public_key(_), do: {:error, "not found key"} def get_public_key_for_ap_id(ap_id) do with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), - {:ok, public_key} <- public_key_from_info(user.info) do + {:ok, public_key} <- public_key(user) do {:ok, public_key} else _ -> :error @@ -1328,7 +1436,7 @@ defmodule Pleroma.User do end def ap_enabled?(%User{local: true}), do: true - def ap_enabled?(%User{info: info}), do: info.ap_enabled + def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled def ap_enabled?(_), do: false @doc "Gets or fetch a user by uri or nickname." @@ -1486,7 +1594,7 @@ defmodule Pleroma.User do left_join: a in Pleroma.Activity, on: u.ap_id == a.actor, where: not is_nil(u.nickname), - where: fragment("not (?->'deactivated' @> 'true')", u.info), + where: u.deactivated != ^true, where: u.id not in ^has_read_notifications, group_by: u.id, having: @@ -1500,16 +1608,16 @@ defmodule Pleroma.User do ## Examples - iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true) - Pleroma.User{info: %{email_notifications: %{"digest" => true}}} + iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => false}}, "digest", true) + Pleroma.User{email_notifications: %{"digest" => true}} - iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false) - Pleroma.User{info: %{email_notifications: %{"digest" => false}}} + iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => true}}, "digest", false) + Pleroma.User{email_notifications: %{"digest" => false}} """ @spec switch_email_notifications(t(), String.t(), boolean()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} def switch_email_notifications(user, type, status) do - update_info(user, &User.Info.update_email_notifications(&1, %{type => status})) + User.update_email_notifications(user, %{type => status}) end @doc """ @@ -1529,17 +1637,16 @@ defmodule Pleroma.User do @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()} def toggle_confirmation(%User{} = user) do - need_confirmation? = !user.info.confirmation_pending - user - |> update_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?)) + |> confirmation_changeset(need_confirmation: !user.confirmation_pending) + |> update_and_set_cache() end - def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do + def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do mascot end - def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do + def get_mascot(%{mascot: mascot}) when is_nil(mascot) do # use instance-default config = Pleroma.Config.get([:assets, :mascots]) default_mascot = Pleroma.Config.get([:assets, :default_mascot]) @@ -1609,6 +1716,31 @@ defmodule Pleroma.User do |> update_and_set_cache() end + # Internal function; public one is `deactivate/2` + defp set_activation_status(user, deactivated) do + user + |> cast(%{deactivated: deactivated}, [:deactivated]) + |> update_and_set_cache() + end + + def update_banner(user, banner) do + user + |> cast(%{banner: banner}, [:banner]) + |> update_and_set_cache() + end + + def update_background(user, background) do + user + |> cast(%{background: background}, [:background]) + |> update_and_set_cache() + end + + def update_source_data(user, source_data) do + user + |> cast(%{source_data: source_data}, [:source_data]) + |> update_and_set_cache() + end + @doc """ Changes `user.info` and returns the user changeset. @@ -1630,4 +1762,160 @@ defmodule Pleroma.User do |> change_info(fun) |> update_and_set_cache() end + + def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do + %{ + admin: is_admin, + moderator: is_moderator + } + end + + # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. + # For example: [{"name": "Pronoun", "value": "she/her"}, …] + def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do + limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0) + + attachment + |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) + |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) + |> Enum.take(limit) + end + + def fields(%{fields: nil}), do: [] + + def fields(%{fields: fields}), do: fields + + def validate_fields(changeset, remote? \\ false) do + limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields + limit = Pleroma.Config.get([:instance, limit_name], 0) + + changeset + |> validate_length(:fields, max: limit) + |> validate_change(:fields, fn :fields, fields -> + if Enum.all?(fields, &valid_field?/1) do + [] + else + [fields: "invalid"] + end + end) + end + + defp valid_field?(%{"name" => name, "value" => value}) do + name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255) + value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255) + + is_binary(name) && is_binary(value) && String.length(name) <= name_limit && + String.length(value) <= value_limit + end + + defp valid_field?(_), do: false + + defp truncate_field(%{"name" => name, "value" => value}) do + {name, _chopped} = + String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255)) + + {value, _chopped} = + String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255)) + + %{"name" => name, "value" => value} + end + + def admin_api_update(user, params) do + user + |> cast(params, [ + :is_moderator, + :is_admin, + :show_role + ]) + |> update_and_set_cache() + end + + def mascot_update(user, url) do + user + |> cast(%{mascot: url}, [:mascot]) + |> validate_required([:mascot]) + |> update_and_set_cache() + end + + def mastodon_settings_update(user, settings) do + user + |> cast(%{settings: settings}, [:settings]) + |> validate_required([:settings]) + |> update_and_set_cache() + end + + @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t() + def confirmation_changeset(user, need_confirmation: need_confirmation?) do + params = + if need_confirmation? do + %{ + confirmation_pending: true, + confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() + } + else + %{ + confirmation_pending: false, + confirmation_token: nil + } + end + + cast(user, params, [:confirmation_pending, :confirmation_token]) + end + + def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do + if id not in user.pinned_activities do + max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0) + params = %{pinned_activities: user.pinned_activities ++ [id]} + + user + |> cast(params, [:pinned_activities]) + |> validate_length(:pinned_activities, + max: max_pinned_statuses, + message: "You have already pinned the maximum number of statuses" + ) + else + change(user) + end + |> update_and_set_cache() + end + + def remove_pinnned_activity(user, %Pleroma.Activity{id: id}) do + params = %{pinned_activities: List.delete(user.pinned_activities, id)} + + user + |> cast(params, [:pinned_activities]) + |> update_and_set_cache() + end + + def update_email_notifications(user, settings) do + email_notifications = + user.email_notifications + |> Map.merge(settings) + |> Map.take(["digest"]) + + params = %{email_notifications: email_notifications} + fields = [:email_notifications] + + user + |> cast(params, fields) + |> validate_required(fields) + |> update_and_set_cache() + end + + defp set_subscribers(user, subscribers) do + params = %{subscribers: subscribers} + + user + |> cast(params, [:subscribers]) + |> validate_required([:subscribers]) + |> update_and_set_cache() + end + + def add_to_subscribers(user, subscribed) do + set_subscribers(user, Enum.uniq([subscribed | user.subscribers])) + end + + def remove_from_subscribers(user, subscribed) do + set_subscribers(user, List.delete(user.subscribers, subscribed)) + end end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 4b5b43d7f..fe59ddeab 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -11,62 +11,11 @@ defmodule Pleroma.User.Info do @type t :: %__MODULE__{} embedded_schema do - field(:banner, :map, default: %{}) - field(:background, :map, default: %{}) - field(:source_data, :map, default: %{}) - field(:note_count, :integer, default: 0) - field(:follower_count, :integer, default: 0) - # Should be filled in only for remote users - field(:following_count, :integer, default: nil) - field(:locked, :boolean, default: false) - field(:confirmation_pending, :boolean, default: false) - field(:password_reset_pending, :boolean, default: false) - field(:confirmation_token, :string, default: nil) - field(:default_scope, :string, default: "public") field(:blocks, {:array, :string}, default: []) field(:domain_blocks, {:array, :string}, default: []) field(:mutes, {:array, :string}, default: []) field(:muted_reblogs, {:array, :string}, default: []) field(:muted_notifications, {:array, :string}, default: []) - field(:subscribers, {:array, :string}, default: []) - field(:deactivated, :boolean, default: false) - field(:no_rich_text, :boolean, default: false) - field(:ap_enabled, :boolean, default: false) - field(:is_moderator, :boolean, default: false) - field(:is_admin, :boolean, default: false) - field(:show_role, :boolean, default: true) - field(:keys, :string, default: nil) - field(:settings, :map, default: nil) - field(:magic_key, :string, default: nil) - field(:uri, :string, default: nil) - field(:topic, :string, default: nil) - field(:hub, :string, default: nil) - field(:salmon, :string, default: nil) - field(:hide_followers_count, :boolean, default: false) - field(:hide_follows_count, :boolean, default: false) - field(:hide_followers, :boolean, default: false) - field(:hide_follows, :boolean, default: false) - field(:hide_favorites, :boolean, default: true) - field(:unread_conversation_count, :integer, default: 0) - field(:pinned_activities, {:array, :string}, default: []) - field(:email_notifications, :map, default: %{"digest" => false}) - field(:mascot, :map, default: nil) - field(:emoji, {:array, :map}, default: []) - field(:pleroma_settings_store, :map, default: %{}) - field(:fields, {:array, :map}, default: nil) - field(:raw_fields, {:array, :map}, default: []) - field(:discoverable, :boolean, default: false) - - field(:notification_settings, :map, - default: %{ - "followers" => true, - "follows" => true, - "non_follows" => true, - "non_followers" => true - } - ) - - field(:skip_thread_containment, :boolean, default: false) # Found in the wild # ap_id -> Where is this used? @@ -77,84 +26,6 @@ defmodule Pleroma.User.Info do # subject _> Where is this used? end - def set_activation_status(info, deactivated) do - params = %{deactivated: deactivated} - - info - |> cast(params, [:deactivated]) - |> validate_required([:deactivated]) - end - - def set_password_reset_pending(info, pending) do - params = %{password_reset_pending: pending} - - info - |> cast(params, [:password_reset_pending]) - |> validate_required([:password_reset_pending]) - end - - def update_notification_settings(info, settings) do - settings = - settings - |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end) - |> Map.new() - - notification_settings = - info.notification_settings - |> Map.merge(settings) - |> Map.take(["followers", "follows", "non_follows", "non_followers"]) - - params = %{notification_settings: notification_settings} - - info - |> cast(params, [:notification_settings]) - |> validate_required([:notification_settings]) - end - - @doc """ - Update email notifications in the given User.Info struct. - - Examples: - - iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true}) - %Pleroma.User.Info{email_notifications: %{"digest" => true}} - - """ - @spec update_email_notifications(t(), map()) :: Ecto.Changeset.t() - def update_email_notifications(info, settings) do - email_notifications = - info.email_notifications - |> Map.merge(settings) - |> Map.take(["digest"]) - - params = %{email_notifications: email_notifications} - fields = [:email_notifications] - - info - |> cast(params, fields) - |> validate_required(fields) - end - - def add_to_note_count(info, number) do - set_note_count(info, info.note_count + number) - end - - def set_note_count(info, number) do - params = %{note_count: Enum.max([0, number])} - - info - |> cast(params, [:note_count]) - |> validate_required([:note_count]) - end - - def set_follower_count(info, number) do - params = %{follower_count: Enum.max([0, number])} - - info - |> cast(params, [:follower_count]) - |> validate_required([:follower_count]) - end - def set_mutes(info, mutes) do params = %{mutes: mutes} @@ -181,14 +52,6 @@ defmodule Pleroma.User.Info do |> validate_required([:blocks]) end - def set_subscribers(info, subscribers) do - params = %{subscribers: subscribers} - - info - |> cast(params, [:subscribers]) - |> validate_required([:subscribers]) - end - @spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t() def add_to_mutes(info, muted, notifications?) do info @@ -214,14 +77,6 @@ defmodule Pleroma.User.Info do set_blocks(info, List.delete(info.blocks, blocked)) end - def add_to_subscribers(info, subscribed) do - set_subscribers(info, Enum.uniq([subscribed | info.subscribers])) - end - - def remove_from_subscribers(info, subscribed) do - set_subscribers(info, List.delete(info.subscribers, subscribed)) - end - def set_domain_blocks(info, domain_blocks) do params = %{domain_blocks: domain_blocks} @@ -238,205 +93,6 @@ defmodule Pleroma.User.Info do set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked)) end - def set_keys(info, keys) do - params = %{keys: keys} - - info - |> cast(params, [:keys]) - |> validate_required([:keys]) - end - - def remote_user_creation(info, params) do - params = - if Map.has_key?(params, :fields) do - Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1)) - else - params - end - - info - |> cast(params, [ - :ap_enabled, - :source_data, - :banner, - :locked, - :magic_key, - :uri, - :hub, - :topic, - :salmon, - :hide_followers, - :hide_follows, - :hide_followers_count, - :hide_follows_count, - :follower_count, - :fields, - :following_count, - :discoverable - ]) - |> validate_fields(true) - end - - def user_upgrade(info, params, remote? \\ false) do - info - |> cast(params, [ - :ap_enabled, - :source_data, - :banner, - :locked, - :magic_key, - :follower_count, - :following_count, - :hide_follows, - :fields, - :hide_followers, - :discoverable, - :hide_followers_count, - :hide_follows_count - ]) - |> validate_fields(remote?) - end - - def profile_update(info, params) do - info - |> cast(params, [ - :locked, - :no_rich_text, - :default_scope, - :banner, - :hide_follows, - :hide_followers, - :hide_followers_count, - :hide_follows_count, - :hide_favorites, - :background, - :show_role, - :skip_thread_containment, - :fields, - :raw_fields, - :pleroma_settings_store, - :discoverable - ]) - |> validate_fields() - end - - def validate_fields(changeset, remote? \\ false) do - limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields - limit = Pleroma.Config.get([:instance, limit_name], 0) - - changeset - |> validate_length(:fields, max: limit) - |> validate_change(:fields, fn :fields, fields -> - if Enum.all?(fields, &valid_field?/1) do - [] - else - [fields: "invalid"] - end - end) - end - - defp valid_field?(%{"name" => name, "value" => value}) do - name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255) - value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255) - - is_binary(name) && is_binary(value) && String.length(name) <= name_limit && - String.length(value) <= value_limit - end - - defp valid_field?(_), do: false - - defp truncate_field(%{"name" => name, "value" => value}) do - {name, _chopped} = - String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255)) - - {value, _chopped} = - String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255)) - - %{"name" => name, "value" => value} - end - - @spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t() - def confirmation_changeset(info, opts) do - need_confirmation? = Keyword.get(opts, :need_confirmation) - - params = - if need_confirmation? do - %{ - confirmation_pending: true, - confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() - } - else - %{ - confirmation_pending: false, - confirmation_token: nil - } - end - - cast(info, params, [:confirmation_pending, :confirmation_token]) - end - - def mastodon_settings_update(info, settings) do - params = %{settings: settings} - - info - |> cast(params, [:settings]) - |> validate_required([:settings]) - end - - def mascot_update(info, url) do - params = %{mascot: url} - - info - |> cast(params, [:mascot]) - |> validate_required([:mascot]) - end - - def set_source_data(info, source_data) do - params = %{source_data: source_data} - - info - |> cast(params, [:source_data]) - |> validate_required([:source_data]) - end - - def admin_api_update(info, params) do - info - |> cast(params, [ - :is_moderator, - :is_admin, - :show_role - ]) - end - - def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do - if id not in info.pinned_activities do - max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0) - params = %{pinned_activities: info.pinned_activities ++ [id]} - - info - |> cast(params, [:pinned_activities]) - |> validate_length(:pinned_activities, - max: max_pinned_statuses, - message: "You have already pinned the maximum number of statuses" - ) - else - change(info) - end - end - - def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do - params = %{pinned_activities: List.delete(info.pinned_activities, id)} - - cast(info, params, [:pinned_activities]) - end - - def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do - %{ - admin: is_admin, - moderator: is_moderator - } - end - def add_reblog_mute(info, ap_id) do params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]} @@ -448,31 +104,4 @@ defmodule Pleroma.User.Info do cast(info, params, [:muted_reblogs]) end - - # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. - # For example: [{"name": "Pronoun", "value": "she/her"}, …] - def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do - limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0) - - attachment - |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) - |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) - |> Enum.take(limit) - end - - def fields(%{fields: nil}), do: [] - - def fields(%{fields: fields}), do: fields - - def follow_information_update(info, params) do - info - |> cast(params, [ - :hide_followers, - :hide_follows, - :follower_count, - :following_count, - :hide_followers_count, - :hide_follows_count - ]) - end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 2baf016cf..7f5273c4e 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -56,7 +56,6 @@ defmodule Pleroma.User.Query do @ilike_criteria [:nickname, :name, :query] @equal_criteria [:email] - @role_criteria [:is_admin, :is_moderator] @contains_criteria [:ap_id, :nickname] @spec build(criteria()) :: Query.t() @@ -100,15 +99,19 @@ defmodule Pleroma.User.Query do Enum.reduce(tags, query, &prepare_tag_criteria/2) end - defp compose_query({key, _}, query) when key in @role_criteria do - where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key))) + defp compose_query({:is_admin, _}, query) do + where(query, [u], u.is_admin) + end + + defp compose_query({:is_moderator, _}, query) do + where(query, [u], u.is_moderator) end defp compose_query({:super_users, _}, query) do where( query, [u], - fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info) + u.is_admin or u.is_moderator ) end @@ -117,7 +120,13 @@ defmodule Pleroma.User.Query do defp compose_query({:external, _}, query), do: location_query(query, false) defp compose_query({:active, _}, query) do - where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info)) + User.restrict_deactivated(query) + |> where([u], not is_nil(u.nickname)) + end + + defp compose_query({:legacy_active, _}, query) do + query + |> where([u], fragment("not (?->'deactivated' @> 'true')", u.info)) |> where([u], not is_nil(u.nickname)) end @@ -126,7 +135,7 @@ defmodule Pleroma.User.Query do end defp compose_query({:deactivated, true}, query) do - where(query, [u], fragment("?->'deactivated' @> 'true'", u.info)) + where(query, [u], u.deactivated == ^true) |> where([u], not is_nil(u.nickname)) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 364452b5d..78efaae2a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -68,7 +68,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp check_actor_is_active(actor) do if not is_nil(actor) do with user <- User.get_cached_by_ap_id(actor), - false <- user.info.deactivated do + false <- user.deactivated do true else _e -> false @@ -601,7 +601,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_thread_visibility( query, - %{"user" => %User{info: %{skip_thread_containment: true}}}, + %{"user" => %User{skip_thread_containment: true}}, _ ), do: query @@ -639,7 +639,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Map.put("user", reading_user) |> Map.put("actor_id", user.ap_id) |> Map.put("whole_db", true) - |> Map.put("pinned_activity_ids", user.info.pinned_activities) + |> Map.put("pinned_activity_ids", user.pinned_activities) recipients = user_activities_recipients(%{ @@ -1049,14 +1049,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do user_data = %{ ap_id: data["id"], - info: %{ - ap_enabled: true, - source_data: data, - banner: banner, - fields: fields, - locked: locked, - discoverable: discoverable - }, + banner: banner, + discoverable: discoverable, + source_data: data, + fields: fields, + locked: locked, + ap_enabled: true, avatar: avatar, name: data["name"], follower_address: data["followers"], diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 080030eb5..568623318 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do with %User{} = user <- User.get_cached_by_nickname(nickname), {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_follows, true} <- - {:show_follows, (for_user && for_user == user) || !user.info.hide_follows} do + {:show_follows, (for_user && for_user == user) || !user.hide_follows} do {page, _} = Integer.parse(page) conn @@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do with %User{} = user <- User.get_cached_by_nickname(nickname), {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_followers, true} <- - {:show_followers, (for_user && for_user == user) || !user.info.hide_followers} do + {:show_followers, (for_user && for_user == user) || !user.hide_followers} do {page, _} = Integer.parse(page) conn @@ -387,7 +387,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do def handle_user_activity(user, %{"type" => "Delete"} = params) do with %Object{} = object <- Object.normalize(params["object"]), - true <- user.info.is_moderator || user.ap_id == object.data["actor"], + true <- user.is_moderator || user.ap_id == object.data["actor"], {:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} else diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index b90193ca0..8abe18e29 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do # has the user successfully posted before? defp old_user?(%User{} = u) do - u.info.note_count > 0 || u.info.follower_count > 0 + u.note_count > 0 || u.follower_count > 0 end # does the post contain links? diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 3866dacee..ebbc1e970 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -140,7 +140,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do |> Enum.map(& &1.ap_id) end - defp maybe_use_sharedinbox(%User{info: %{source_data: data}}), + defp maybe_use_sharedinbox(%User{source_data: data}), do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] @doc """ @@ -156,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do """ def determine_inbox( %Activity{data: activity_data}, - %User{info: %{source_data: data}} = user + %User{source_data: data} = user ) do to = activity_data["to"] || [] cc = activity_data["cc"] || [] @@ -190,12 +190,12 @@ defmodule Pleroma.Web.ActivityPub.Publisher do recipients |> Enum.filter(&User.ap_enabled?/1) - |> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end) + |> Enum.map(fn %{source_data: data} -> data["inbox"] end) |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) |> Instances.filter_reachable() |> Enum.each(fn {inbox, unreachable_since} -> %User{ap_id: ap_id} = - Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end) + Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end) # Get all the recipients on the same host and add them to cc. Otherwise, a remote # instance would only accept a first message for the first recipient and ignore the rest. diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b56343beb..d0ad270c2 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -600,9 +600,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) - banner = new_user_data[:info][:banner] - locked = new_user_data[:info][:locked] || false - attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || [] + locked = new_user_data[:locked] || false + attachment = get_in(new_user_data, [:source_data, "attachment"]) || [] fields = attachment @@ -611,8 +610,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do update_data = new_user_data - |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, %{banner: banner, locked: locked, fields: fields}) + |> Map.take([:avatar, :banner, :bio, :name]) + |> Map.put(:fields, fields) + |> Map.put(:locked, locked) actor |> User.upgrade_changeset(update_data, true) @@ -979,7 +979,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Mention", "href" => ap_id, "name" => "@#{nickname}"} end - def take_emoji_tags(%User{info: %{emoji: emoji} = _user_info} = _user) do + def take_emoji_tags(%User{emoji: emoji}) do emoji |> Enum.flat_map(&Map.to_list/1) |> Enum.map(&build_emoji_tag/1) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 9b39d1629..82879a486 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -78,8 +78,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do emoji_tags = Transmogrifier.take_emoji_tags(user) fields = - user.info - |> User.Info.fields() + user + |> User.fields() |> Enum.map(fn %{"name" => name, "value" => value} -> %{ "name" => Pleroma.HTML.strip_tags(name), @@ -99,7 +99,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "name" => user.name, "summary" => user.bio, "url" => user.ap_id, - "manuallyApprovesFollowers" => user.info.locked, + "manuallyApprovesFollowers" => user.locked, "publicKey" => %{ "id" => "#{user.ap_id}#main-key", "owner" => user.ap_id, @@ -107,8 +107,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do }, "endpoints" => endpoints, "attachment" => fields, - "tag" => (user.info.source_data["tag"] || []) ++ emoji_tags, - "discoverable" => user.info.discoverable + "tag" => (user.source_data["tag"] || []) ++ emoji_tags, + "discoverable" => user.discoverable } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) @@ -116,8 +116,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do end def render("following.json", %{user: user, page: page} = opts) do - showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows - showing_count = showing_items || !user.info.hide_follows_count + showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows + showing_count = showing_items || !user.hide_follows_count query = User.get_friends_query(user) query = from(user in query, select: [:ap_id]) @@ -135,8 +135,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do end def render("following.json", %{user: user} = opts) do - showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows - showing_count = showing_items || !user.info.hide_follows_count + showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows + showing_count = showing_items || !user.hide_follows_count query = User.get_friends_query(user) query = from(user in query, select: [:ap_id]) @@ -155,7 +155,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "totalItems" => total, "first" => if showing_items do - collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows) + collection(following, "#{user.ap_id}/following", 1, !user.hide_follows) else "#{user.ap_id}/following?page=1" end @@ -164,8 +164,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do end def render("followers.json", %{user: user, page: page} = opts) do - showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers - showing_count = showing_items || !user.info.hide_followers_count + showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers + showing_count = showing_items || !user.hide_followers_count query = User.get_followers_query(user) query = from(user in query, select: [:ap_id]) @@ -183,8 +183,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do end def render("followers.json", %{user: user} = opts) do - showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers - showing_count = showing_items || !user.info.hide_followers_count + showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers + showing_count = showing_items || !user.hide_followers_count query = User.get_followers_query(user) query = from(user in query, select: [:ap_id]) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 513bae800..3894b3742 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -234,9 +234,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do user = User.get_cached_by_nickname(nickname) - {:ok, updated_user} = User.deactivate(user, !user.info.deactivated) + {:ok, updated_user} = User.deactivate(user, !user.deactivated) - action = if user.info.deactivated, do: "activate", else: "deactivate" + action = if user.deactivated, do: "activate", else: "deactivate" ModerationLog.insert_log(%{ actor: admin, @@ -318,12 +318,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do "nickname" => nickname }) when permission_group in ["moderator", "admin"] do - info = Map.put(%{}, "is_" <> permission_group, true) + fields = %{:"is_#{permission_group}" => true} {:ok, user} = nickname |> User.get_cached_by_nickname() - |> User.update_info(&User.Info.admin_api_update(&1, info)) + |> User.admin_api_update(fields) ModerationLog.insert_log(%{ action: "grant", @@ -332,7 +332,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do permission: permission_group }) - json(conn, info) + json(conn, fields) end def right_add(conn, _) do @@ -344,8 +344,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do conn |> json(%{ - is_moderator: user.info.is_moderator, - is_admin: user.info.is_admin + is_moderator: user.is_moderator, + is_admin: user.is_admin }) end @@ -361,12 +361,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do } ) when permission_group in ["moderator", "admin"] do - info = Map.put(%{}, "is_" <> permission_group, false) + fields = %{:"is_#{permission_group}" => false} {:ok, user} = nickname |> User.get_cached_by_nickname() - |> User.update_info(&User.Info.admin_api_update(&1, info)) + |> User.admin_api_update(fields) ModerationLog.insert_log(%{ action: "revoke", @@ -375,7 +375,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do permission: permission_group }) - json(conn, info) + json(conn, fields) end def right_delete(conn, _) do @@ -389,7 +389,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do with {:ok, status} <- Ecto.Type.cast(:boolean, status), %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, _} <- User.deactivate(user, !status) do - action = if(user.info.deactivated, do: "activate", else: "deactivate") + action = if(user.deactivated, do: "activate", else: "deactivate") ModerationLog.insert_log(%{ actor: admin, diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index a96affd40..eed1b9fa7 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.AccountView do alias Pleroma.HTML alias Pleroma.User - alias Pleroma.User.Info alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.MediaProxy @@ -28,9 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do "avatar" => avatar, "nickname" => user.nickname, "display_name" => display_name, - "deactivated" => user.info.deactivated, + "deactivated" => user.deactivated, "local" => user.local, - "roles" => Info.roles(user.info), + "roles" => User.roles(user), "tags" => user.tags || [] } end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 386408d51..ef738a870 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -263,10 +263,10 @@ defmodule Pleroma.Web.CommonAPI do # Updates the emojis for a user based on their profile def update(user) do emoji = emoji_from_profile(user) - source_data = user.info |> Map.get(:source_data, %{}) |> Map.put("tag", emoji) + source_data = Map.put(user.source_data, "tag", emoji) user = - case User.update_info(user, &User.Info.set_source_data(&1, source_data)) do + case User.update_source_data(user, source_data) do {:ok, user} -> user _ -> user end @@ -287,20 +287,20 @@ defmodule Pleroma.Web.CommonAPI do object: %Object{data: %{"type" => "Note"}} } = activity <- get_by_id_or_ap_id(id_or_ap_id), true <- Visibility.is_public?(activity), - {:ok, _user} <- User.update_info(user, &User.Info.add_pinnned_activity(&1, activity)) do + {:ok, _user} <- User.add_pinnned_activity(user, activity) do {:ok, activity} else - {:error, %{changes: %{info: %{errors: [pinned_activities: {err, _}]}}}} -> {:error, err} + {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err} _ -> {:error, dgettext("errors", "Could not pin")} end end def unpin(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), - {:ok, _user} <- User.update_info(user, &User.Info.remove_pinnned_activity(&1, activity)) do + {:ok, _user} <- User.remove_pinnned_activity(user, activity) do {:ok, activity} else - %{errors: [pinned_activities: {err, _}]} -> {:error, err} + {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err} _ -> {:error, dgettext("errors", "Could not unpin")} end end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 87860f1d5..cf0fe91ce 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -36,7 +36,7 @@ defmodule Pleroma.Web.MastoFEController do @doc "PUT /api/web/settings" def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do - with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do + with {:ok, _} <- User.mastodon_settings_update(user, settings) do json(conn, %{}) else e -> diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 9ef7fd48d..7ba2f9ff1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -130,25 +130,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do def update_credentials(%{assigns: %{user: original_user}} = conn, params) do user = original_user - user_params = - %{} - |> add_if_present(params, "display_name", :name) - |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end) - |> add_if_present(params, "avatar", :avatar, fn value -> - with %Plug.Upload{} <- value, - {:ok, object} <- ActivityPub.upload(value, type: :avatar) do - {:ok, object.data} - end - end) - - emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") - - user_info_emojis = - user.info - |> Map.get(:emoji, []) - |> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text)) - |> Enum.dedup() - params = if Map.has_key?(params, "fields_attributes") do Map.update!(params, "fields_attributes", fn fields -> @@ -160,7 +141,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do params end - info_params = + user_params = [ :no_rich_text, :locked, @@ -176,15 +157,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> Enum.reduce(%{}, fn key, acc -> add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)}) end) - |> add_if_present(params, "default_scope", :default_scope) - |> add_if_present(params, "fields_attributes", :fields, fn fields -> - fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) - - {:ok, fields} - end) - |> add_if_present(params, "fields_attributes", :raw_fields) - |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> - {:ok, Map.merge(user.info.pleroma_settings_store, value)} + |> add_if_present(params, "display_name", :name) + |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end) + |> add_if_present(params, "avatar", :avatar, fn value -> + with %Plug.Upload{} <- value, + {:ok, object} <- ActivityPub.upload(value, type: :avatar) do + {:ok, object.data} + end end) |> add_if_present(params, "header", :banner, fn value -> with %Plug.Upload{} <- value, @@ -198,12 +177,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:ok, object.data} end end) - |> Map.put(:emoji, user_info_emojis) + |> add_if_present(params, "fields_attributes", :fields, fn fields -> + fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) + + {:ok, fields} + end) + |> add_if_present(params, "fields_attributes", :raw_fields) + |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> + {:ok, Map.merge(user.pleroma_settings_store, value)} + end) + |> add_if_present(params, "default_scope", :default_scope) + + emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") + + user_emojis = + user + |> Map.get(:emoji, []) + |> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text)) + |> Enum.dedup() + + user_params = Map.put(user_params, :emoji, user_emojis) changeset = user |> User.update_changeset(user_params) - |> User.change_info(&User.Info.profile_update(&1, info_params)) + |> User.change_info(& &1) with {:ok, user} <- User.update_and_set_cache(changeset) do if original_user != user, do: CommonAPI.update(user) @@ -269,7 +267,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do followers = cond do for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params) - user.info.hide_followers -> [] + user.hide_followers -> [] true -> MastodonAPI.get_followers(user, params) end @@ -283,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do followers = cond do for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params) - user.info.hide_follows -> [] + user.hide_follows -> [] true -> MastodonAPI.get_friends(user, params) end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 2d4976891..e30fed610 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -74,23 +74,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do user_info = User.get_cached_user_info(user) following_count = - if !user.info.hide_follows_count or !user.info.hide_follows or opts[:for] == user do + if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do user_info.following_count else 0 end followers_count = - if !user.info.hide_followers_count or !user.info.hide_followers or opts[:for] == user do + if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do user_info.follower_count else 0 end - bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"] + bot = (user.source_data["type"] || "Person") in ["Application", "Service"] emojis = - (user.info.source_data["tag"] || []) + (user.source_data["tag"] || []) |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> %{ @@ -102,8 +102,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do end) fields = - user.info - |> User.Info.fields() + user + |> User.fields() |> Enum.map(fn %{"name" => name, "value" => value} -> %{ "name" => Pleroma.HTML.strip_tags(name), @@ -111,23 +111,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do } end) - raw_fields = Map.get(user.info, :raw_fields, []) - bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for])) relationship = render("relationship.json", %{user: opts[:for], target: user}) - discoverable = user.info.discoverable - %{ id: to_string(user.id), username: username_from_nickname(user.nickname), acct: user.nickname, display_name: display_name, - locked: user_info.locked, + locked: user.locked, created_at: Utils.to_masto_date(user.inserted_at), followers_count: followers_count, following_count: following_count, - statuses_count: user_info.note_count, + statuses_count: user.note_count, note: bio || "", url: User.profile_url(user), avatar: image, @@ -140,9 +136,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do source: %{ note: HTML.strip_tags((user.bio || "") |> String.replace("
", "\n")), sensitive: false, - fields: raw_fields, + fields: user.raw_fields, pleroma: %{ - discoverable: discoverable + discoverable: user.discoverable } }, @@ -150,14 +146,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do pleroma: %{ confirmation_pending: user_info.confirmation_pending, tags: user.tags, - hide_followers_count: user.info.hide_followers_count, - hide_follows_count: user.info.hide_follows_count, - hide_followers: user.info.hide_followers, - hide_follows: user.info.hide_follows, - hide_favorites: user.info.hide_favorites, + hide_followers_count: user.hide_followers_count, + hide_follows_count: user.hide_follows_count, + hide_followers: user.hide_followers, + hide_follows: user.hide_follows, + hide_favorites: user.hide_favorites, relationship: relationship, - skip_thread_containment: user.info.skip_thread_containment, - background_image: image_url(user.info.background) |> MediaProxy.url() + skip_thread_containment: user.skip_thread_containment, + background_image: image_url(user.background) |> MediaProxy.url() } } |> maybe_put_role(user, opts[:for]) @@ -195,21 +191,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do data, %User{id: user_id} = user, %User{id: user_id}, - user_info + _user_info ) do data - |> Kernel.put_in([:source, :privacy], user_info.default_scope) - |> Kernel.put_in([:source, :pleroma, :show_role], user.info.show_role) - |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.info.no_rich_text) + |> Kernel.put_in([:source, :privacy], user.default_scope) + |> Kernel.put_in([:source, :pleroma, :show_role], user.show_role) + |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.no_rich_text) end defp maybe_put_settings(data, _, _, _), do: data - defp maybe_put_settings_store(data, %User{info: info, id: id}, %User{id: id}, %{ + defp maybe_put_settings_store(data, %User{} = user, %User{}, %{ with_pleroma_settings: true }) do data - |> Kernel.put_in([:pleroma, :settings_store], info.pleroma_settings_store) + |> Kernel.put_in([:pleroma, :settings_store], user.pleroma_settings_store) end defp maybe_put_settings_store(data, _, _, _), do: data @@ -223,28 +219,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_chat_token(data, _, _, _), do: data - defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do + defp maybe_put_role(data, %User{show_role: true} = user, _) do data - |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin) - |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator) + |> Kernel.put_in([:pleroma, :is_admin], user.is_admin) + |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator) end defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do data - |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin) - |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator) + |> Kernel.put_in([:pleroma, :is_admin], user.is_admin) + |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator) end defp maybe_put_role(data, _, _), do: data defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do - Kernel.put_in(data, [:pleroma, :notification_settings], user.info.notification_settings) + Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings) end defp maybe_put_notification_settings(data, _, _), do: data - defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do - Kernel.put_in(data, [:pleroma, :deactivated], user.info.deactivated) + defp maybe_put_activation_status(data, user, %User{is_admin: true}) do + Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated) end defp maybe_put_activation_status(data, _, _), do: data @@ -253,7 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do data |> Kernel.put_in( [:pleroma, :unread_conversation_count], - user.info.unread_conversation_count + user.unread_conversation_count ) end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 9b8dd3086..b785ca9d4 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -498,6 +498,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do defp present?(false), do: false defp present?(_), do: true - defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}), + defp pinned?(%Activity{id: id}, %User{pinned_activities: pinned_activities}), do: id in pinned_activities end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 03c9a5027..fe71aca8c 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -202,9 +202,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do with {:ok, %User{} = user} <- Authenticator.get_user(conn), {:ok, app} <- Token.Utils.fetch_app(conn), {:auth_active, true} <- {:auth_active, User.auth_active?(user)}, - {:user_active, true} <- {:user_active, !user.info.deactivated}, + {:user_active, true} <- {:user_active, !user.deactivated}, {:password_reset_pending, false} <- - {:password_reset_pending, user.info.password_reset_pending}, + {:password_reset_pending, user.password_reset_pending}, {:ok, scopes} <- validate_scopes(app, params), {:ok, auth} <- Authorization.create_authorization(app, user, scopes), {:ok, token} <- Token.exchange_token(app, auth) do diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex index 24513972e..483099d34 100644 --- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.OStatus.FollowHandler do followed_uri when not is_nil(followed_uri) <- XML.string_from_xpath("/entry/activity:object/id", entry), {:ok, followed} <- OStatus.find_or_make_user(followed_uri), - {:locked, false} <- {:locked, followed.info.locked}, + {:locked, false} <- {:locked, followed.locked}, {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do User.follow(actor, followed) {:ok, activity} diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 5de1ceef3..a11cacacc 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -287,14 +287,19 @@ defmodule Pleroma.Web.OStatus do end defp build_user_data(info) do - %{ + info_fields = Enum.map(User.info_fields(), &to_string/1) + + info + |> Map.take(info_fields) + |> Map.new(fn {k, v} -> {String.to_atom(k), v} end) + |> Map.merge(%{ name: info["name"], nickname: info["nickname"] <> "@" <> info["host"], ap_id: info["uri"], info: info, avatar: info["avatar"], bio: info["bio"] - } + }) end # TODO: Just takes the first one for now. diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 9012e2175..ee40bbf33 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -80,9 +80,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do @doc "PATCH /api/v1/pleroma/accounts/update_banner" def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do - new_info = %{"banner" => %{}} - - with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do + with {:ok, user} <- User.update_banner(user, %{}) do CommonAPI.update(user) json(conn, %{url: nil}) end @@ -90,8 +88,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do def update_banner(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), - new_info <- %{"banner" => object.data}, - {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do + {:ok, user} <- User.update_banner(user, object.data) do CommonAPI.update(user) %{"url" => [%{"href" => href} | _]} = object.data @@ -101,17 +98,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do @doc "PATCH /api/v1/pleroma/accounts/update_background" def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do - new_info = %{"background" => %{}} - - with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do + with {:ok, _user} <- User.update_background(user, %{}) do json(conn, %{url: nil}) end end def update_background(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(params, type: :background), - new_info <- %{"background" => object.data}, - {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do + {:ok, _user} <- User.update_background(user, object.data) do %{"url" => [%{"href" => href} | _]} = object.data json(conn, %{url: href}) @@ -119,7 +113,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do end @doc "GET /api/v1/pleroma/accounts/:id/favourites" - def favourites(%{assigns: %{account: %{info: %{hide_favorites: true}}}} = conn, _params) do + def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params) do render_error(conn, :forbidden, "Can't get favorites") end diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index d71d72dd5..8cf552b7e 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -24,9 +24,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)), # Reject if not an image %{type: "image"} = attachment <- render_attachment(object) do - # Sure! - # Save to the user's info - {:ok, _user} = User.update_info(user, &User.Info.mascot_update(&1, attachment)) + {:ok, _user} = User.mascot_update(user, attachment) json(conn, attachment) else diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 0ffe903cd..0361c18da 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -147,7 +147,7 @@ defmodule Pleroma.Web.Salmon do end @doc "Pushes an activity to remote account." - def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params), + def publish_one(%{recipient: %{salmon: salmon}} = params), do: publish_one(Map.put(params, :recipient, salmon)) def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do @@ -217,19 +217,19 @@ defmodule Pleroma.Web.Salmon do remote_users = remote_users(user, activity) - salmon_urls = Enum.map(remote_users, & &1.info.salmon) + salmon_urls = Enum.map(remote_users, & &1.salmon) reachable_urls_metadata = Instances.filter_reachable(salmon_urls) reachable_urls = Map.keys(reachable_urls_metadata) remote_users - |> Enum.filter(&(&1.info.salmon in reachable_urls)) + |> Enum.filter(&(&1.salmon in reachable_urls)) |> Enum.each(fn remote_user -> Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) Publisher.enqueue_one(__MODULE__, %{ recipient_id: remote_user.id, feed: feed, - unreachable_since: reachable_urls_metadata[remote_user.info.salmon] + unreachable_since: reachable_urls_metadata[remote_user.salmon] }) end) end diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index 0ea224874..9910c3484 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -212,7 +212,7 @@ defmodule Pleroma.Web.Streamer.Worker do end @spec thread_containment(Activity.t(), User.t()) :: boolean() - defp thread_containment(_activity, %User{info: %{skip_thread_containment: true}}), do: true + defp thread_containment(_activity, %User{skip_thread_containment: true}), do: true defp thread_containment(activity, user) do if Config.get([:instance, :skip_thread_containment]) do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index bf5a6ae42..5eafc01de 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -20,11 +20,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do action_fallback(:errors) def confirm_email(conn, %{"user_id" => uid, "token" => token}) do - new_info = [need_confirmation: false] - - with %User{info: info} = user <- User.get_cached_by_id(uid), - true <- user.local and info.confirmation_pending and info.confirmation_token == token, - {:ok, _} <- User.update_info(user, &User.Info.confirmation_changeset(&1, new_info)) do + with %User{} = user <- User.get_cached_by_id(uid), + true <- user.local and user.confirmation_pending and user.confirmation_token == token, + {:ok, _} <- + User.update_and_set_cache(User.confirmation_changeset(user, need_confirmation: false)) do redirect(conn, to: "/") end end diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex index 21b086d4c..10cba9b3b 100644 --- a/lib/pleroma/web/views/masto_fe_view.ex +++ b/lib/pleroma/web/views/masto_fe_view.ex @@ -61,12 +61,12 @@ defmodule Pleroma.Web.MastoFEView do }, poll_limits: Config.get([:instance, :poll_limits]), rights: %{ - delete_others_notice: present?(user.info.is_moderator), - admin: present?(user.info.is_admin) + delete_others_notice: present?(user.is_moderator), + admin: present?(user.is_admin) }, compose: %{ me: "#{user.id}", - default_privacy: user.info.default_scope, + default_privacy: user.default_scope, default_sensitive: false, allow_content_types: Config.get([:instance, :allowed_post_formats]) }, @@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastoFEView do "video\/mp4" ] }, - settings: user.info.settings || @default_settings, + settings: user.settings || @default_settings, push_subscription: nil, accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)}, custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis), diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index b61f388b8..0f98ec4fd 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -181,7 +181,7 @@ defmodule Pleroma.Web.Websub do end def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do - topic = subscribed.info.topic + topic = subscribed.topic # FIXME: Race condition, use transactions {:ok, subscription} = with subscription when not is_nil(subscription) <- @@ -193,7 +193,7 @@ defmodule Pleroma.Web.Websub do _e -> subscription = %WebsubClientSubscription{ topic: topic, - hub: subscribed.info.hub, + hub: subscribed.hub, subscribers: [subscriber.ap_id], state: "requested", secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(), -- cgit v1.2.3 From 66b5d0ff558bffeddf6475af72b73bf2870512f6 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Thu, 17 Oct 2019 15:26:59 +0300 Subject: add Markers /api/v1/markers --- lib/pleroma/marker.ex | 74 ++++++++++++++++++++++ .../mastodon_api/controllers/marker_controller.ex | 32 ++++++++++ lib/pleroma/web/mastodon_api/views/marker_view.ex | 17 +++++ lib/pleroma/web/router.ex | 3 + 4 files changed, 126 insertions(+) create mode 100644 lib/pleroma/marker.ex create mode 100644 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex create mode 100644 lib/pleroma/web/mastodon_api/views/marker_view.ex (limited to 'lib') diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex new file mode 100644 index 000000000..7f87c86c3 --- /dev/null +++ b/lib/pleroma/marker.ex @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Marker do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Ecto.Multi + alias Pleroma.Repo + alias Pleroma.User + + @timelines ["notifications"] + + schema "markers" do + field(:last_read_id, :string, default: "") + field(:timeline, :string, default: "") + field(:lock_version, :integer, default: 0) + + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + timestamps() + end + + def get_markers(user, timelines \\ []) do + Repo.all(get_query(user, timelines)) + end + + def upsert(%User{} = user, attrs) do + attrs + |> Map.take(@timelines) + |> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi -> + marker = + user + |> get_marker(timeline) + |> changeset(timeline_attrs) + + Multi.insert(multi, timeline, marker, + returning: true, + on_conflict: {:replace, [:last_read_id]}, + conflict_target: [:user_id, :timeline] + ) + end) + |> Repo.transaction() + end + + defp get_marker(user, timeline) do + case Repo.find_resource(get_query(user, timeline)) do + {:ok, marker} -> %__MODULE__{marker | user: user} + _ -> %__MODULE__{timeline: timeline, user_id: user.id} + end + end + + @doc false + defp changeset(marker, attrs) do + marker + |> cast(attrs, [:last_read_id]) + |> validate_required([:user_id, :timeline, :last_read_id]) + |> validate_inclusion(:timeline, @timelines) + end + + defp by_timeline(query, timeline) do + from(m in query, where: m.timeline in ^List.wrap(timeline)) + end + + defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id) + + defp get_query(user, timelines) do + __MODULE__ + |> by_user_id(user.id) + |> by_timeline(timelines) + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex new file mode 100644 index 000000000..ce025624d --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MarkerController do + use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug + + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"]} + when action == :index + ) + + plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + + # GET /api/v1/markers + def index(%{assigns: %{user: user}} = conn, params) do + markers = Pleroma.Marker.get_markers(user, params["timeline"]) + render(conn, "markers.json", %{markers: markers}) + end + + # POST /api/v1/markers + def upsert(%{assigns: %{user: user}} = conn, params) do + with {:ok, result} <- Pleroma.Marker.upsert(user, params), + markers <- Map.values(result) do + render(conn, "markers.json", %{markers: markers}) + end + end +end diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex new file mode 100644 index 000000000..38fbeed5f --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MarkerView do + use Pleroma.Web, :view + + def render("markers.json", %{markers: markers}) do + Enum.reduce(markers, %{}, fn m, acc -> + Map.put_new(acc, m.timeline, %{ + last_read_id: m.last_read_id, + version: m.lock_version, + updated_at: NaiveDateTime.to_iso8601(m.updated_at) + }) + end) + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ae799b8ac..45684284c 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -394,6 +394,9 @@ defmodule Pleroma.Web.Router do get("/push/subscription", SubscriptionController, :get) put("/push/subscription", SubscriptionController, :update) delete("/push/subscription", SubscriptionController, :delete) + + get("/markers", MarkerController, :index) + post("/markers", MarkerController, :upsert) end scope "/api/web", Pleroma.Web do -- cgit v1.2.3 From 359dd1890e6afcf80584021eaa2421336614dd07 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Thu, 17 Oct 2019 15:25:15 +0300 Subject: Mastodon API: Mark the conversation as read for the author when they send a new direct message --- lib/pleroma/conversation/participation.ex | 6 ++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 23 +++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index ab81f3217..e17f49e58 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -48,6 +48,12 @@ defmodule Pleroma.Conversation.Participation do |> validate_required([:read]) end + def mark_as_read(%User{} = user, %Conversation{} = conversation) do + with %__MODULE__{} = participation <- for_user_and_conversation(user, conversation) do + mark_as_read(participation) + end + end + def mark_as_read(participation) do participation |> read_cng(%{read: true}) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 091ec2588..d391732a2 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity.Ir.Topics alias Pleroma.Config alias Pleroma.Conversation + alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Object.Containment @@ -153,11 +154,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Notification.create_notifications(activity) - participations = - activity - |> Conversation.create_or_bump_for() - |> get_participations() - + conversation = create_or_bump_conversation(activity, map["actor"]) + participations = get_participations(conversation) stream_out(activity) stream_out_participations(participations) {:ok, activity} @@ -182,7 +180,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - defp get_participations({:ok, %{participations: participations}}), do: participations + defp create_or_bump_conversation(activity, actor) do + with {:ok, conversation} <- Conversation.create_or_bump_for(activity), + %User{} = user <- User.get_cached_by_ap_id(actor), + Participation.mark_as_read(user, conversation) do + {:ok, conversation} + end + end + + defp get_participations({:ok, conversation}) do + conversation + |> Repo.preload(:participations, force: true) + |> Map.get(:participations) + end + defp get_participations(_), do: [] def stream_out_participations(participations) do -- cgit v1.2.3 From 733b73b71c2a13a2bfbf4c0a0d35ddfa140a1ce0 Mon Sep 17 00:00:00 2001 From: kaniini Date: Fri, 18 Oct 2019 04:36:37 +0000 Subject: Apply suggestion to lib/pleroma/web/mastodon_api/websocket_handler.ex --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 1a757363f..a400d1c8d 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -36,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do {:ok, user} <- allow_request(stream, [access_token, sec_websocket]), topic when is_binary(topic) <- expand_topic(stream, params) do req = - if sec_websocket != nil do + if sec_websocket do :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req) else req -- cgit v1.2.3 From 3c6fd0bb991c865aad2fb27bc798b2b3c3c248d2 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 18 Oct 2019 12:29:42 +0200 Subject: upload.ex: Remove deprecated configuration --- lib/pleroma/upload.ex | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 9f0adde5b..2e0986197 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -105,7 +105,7 @@ defmodule Pleroma.Upload do {Pleroma.Config.get!([:instance, :upload_limit]), "Document"} end - opts = %{ + %{ activity_type: Keyword.get(opts, :activity_type, activity_type), size_limit: Keyword.get(opts, :size_limit, size_limit), uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])), @@ -118,37 +118,6 @@ defmodule Pleroma.Upload do Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) ) } - - # TODO: 1.0+ : remove old config compatibility - opts = - if Pleroma.Config.get([__MODULE__, :strip_exif]) == true && - !Enum.member?(opts.filters, Pleroma.Upload.Filter.Mogrify) do - Logger.warn(""" - Pleroma: configuration `:instance, :strip_exif` is deprecated, please instead set: - - :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Mogrify]] - - :pleroma, Pleroma.Upload.Filter.Mogrify, args: ["strip", "auto-orient"] - """) - - Pleroma.Config.put([Pleroma.Upload.Filter.Mogrify], args: ["strip", "auto-orient"]) - Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Mogrify]) - else - opts - end - - if Pleroma.Config.get([:instance, :dedupe_media]) == true && - !Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do - Logger.warn(""" - Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set: - - :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]] - """) - - Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe]) - else - opts - end end defp prepare_upload(%Plug.Upload{} = file, opts) do -- cgit v1.2.3 From 39e996528c1e7551675c0d0f140dcfa01e671004 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 18 Oct 2019 14:11:30 +0300 Subject: Fix a migration wiping user info of users that don't have any mutes And introduce safe_jsonb_set --- lib/mix/tasks/pleroma/database.ex | 4 ++-- lib/pleroma/object.ex | 4 ++-- lib/pleroma/user.ex | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index cfd9eeada..8a827ca80 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -28,7 +28,7 @@ defmodule Mix.Tasks.Pleroma.Database do Logger.info("Removing embedded objects") Repo.query!( - "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;", + "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;", [], timeout: :infinity ) @@ -126,7 +126,7 @@ defmodule Mix.Tasks.Pleroma.Database do set: [ data: fragment( - "jsonb_set(?, '{likes}', '[]'::jsonb, true)", + "safe_jsonb_set(?, '{likes}', '[]'::jsonb, true)", object.data ) ] diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index cdfbacb0e..d9b41d710 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -181,7 +181,7 @@ defmodule Pleroma.Object do data: fragment( """ - jsonb_set(?, '{repliesCount}', + safe_jsonb_set(?, '{repliesCount}', (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) """, o.data, @@ -204,7 +204,7 @@ defmodule Pleroma.Object do data: fragment( """ - jsonb_set(?, '{repliesCount}', + safe_jsonb_set(?, '{repliesCount}', (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true) """, o.data, diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 917bcf2ef..d0d3e1f9a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -727,7 +727,7 @@ defmodule Pleroma.User do set: [ info: fragment( - "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", + "safe_jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", u.info, u.info ) @@ -748,7 +748,7 @@ defmodule Pleroma.User do set: [ info: fragment( - "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", + "safe_jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", u.info, u.info ) @@ -818,7 +818,7 @@ defmodule Pleroma.User do set: [ info: fragment( - "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", + "safe_jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", u.info, s.count ) -- cgit v1.2.3 From 25b7ff56c371fa3b405fa339aaac6d4f0876ed85 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 22:52:46 +0000 Subject: application: don't start Federator.init/1 anymore --- lib/pleroma/application.ex | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 0bf218bc7..d681eecc8 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -161,11 +161,6 @@ defmodule Pleroma.Application do 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 } ] end @@ -177,11 +172,6 @@ defmodule Pleroma.Application do 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 - }, %{ id: :internal_fetch_init, start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]}, -- cgit v1.2.3 From b16a460916e0384ec6ed34f80a390b9a4ed4d96d Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 22:53:45 +0000 Subject: federator: remove websub stuff --- lib/pleroma/web/federator/federator.ex | 42 -------------------------------- lib/pleroma/workers/subscriber_worker.ex | 26 -------------------- 2 files changed, 68 deletions(-) delete mode 100644 lib/pleroma/workers/subscriber_worker.ex (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 1a2da014a..8227d1a3a 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -11,18 +11,11 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.OStatus - alias Pleroma.Web.Websub alias Pleroma.Workers.PublisherWorker alias Pleroma.Workers.ReceiverWorker - alias Pleroma.Workers.SubscriberWorker require Logger - def init do - # To do: consider removing this call in favor of scheduled execution (`quantum`-based) - refresh_subscriptions(schedule_in: 60) - end - @doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)" # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength def allowed_incoming_reply_depth?(depth) do @@ -53,18 +46,6 @@ defmodule Pleroma.Web.Federator do PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}) end - def verify_websub(websub) do - SubscriberWorker.enqueue("verify_websub", %{"websub_id" => websub.id}) - end - - def request_subscription(websub) do - SubscriberWorker.enqueue("request_subscription", %{"websub_id" => websub.id}) - end - - def refresh_subscriptions(worker_args \\ []) do - SubscriberWorker.enqueue("refresh_subscriptions", %{}, worker_args ++ [max_attempts: 1]) - end - # Job Worker Callbacks @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()} @@ -111,29 +92,6 @@ defmodule Pleroma.Web.Federator do end end - def perform(:request_subscription, websub) do - Logger.debug("Refreshing #{websub.topic}") - - with {:ok, websub} <- Websub.request_subscription(websub) do - Logger.debug("Successfully refreshed #{websub.topic}") - else - _e -> Logger.debug("Couldn't refresh #{websub.topic}") - end - end - - def perform(:verify_websub, websub) do - Logger.debug(fn -> - "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})" - end) - - Websub.verify(websub) - end - - def perform(:refresh_subscriptions) do - Logger.debug("Federator running refresh subscriptions") - Websub.refresh_subscriptions() - end - def ap_enabled_actor(id) do user = User.get_cached_by_ap_id(id) diff --git a/lib/pleroma/workers/subscriber_worker.ex b/lib/pleroma/workers/subscriber_worker.ex deleted file mode 100644 index fc490e300..000000000 --- a/lib/pleroma/workers/subscriber_worker.ex +++ /dev/null @@ -1,26 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.SubscriberWorker do - alias Pleroma.Repo - alias Pleroma.Web.Federator - alias Pleroma.Web.Websub - - use Pleroma.Workers.WorkerHelper, queue: "federator_outgoing" - - @impl Oban.Worker - def perform(%{"op" => "refresh_subscriptions"}, _job) do - Federator.perform(:refresh_subscriptions) - end - - def perform(%{"op" => "request_subscription", "websub_id" => websub_id}, _job) do - websub = Repo.get(Websub.WebsubClientSubscription, websub_id) - Federator.perform(:request_subscription, websub) - end - - def perform(%{"op" => "verify_websub", "websub_id" => websub_id}, _job) do - websub = Repo.get(Websub.WebsubServerSubscription, websub_id) - Federator.perform(:verify_websub, websub) - end -end -- cgit v1.2.3 From 4f82e42e4e581b32cd25fff862f880f7f5a87b81 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 22:57:37 +0000 Subject: websub: remove entirely --- lib/pleroma/user.ex | 7 - lib/pleroma/web/activity_pub/transmogrifier.ex | 15 - lib/pleroma/web/ostatus/ostatus.ex | 5 +- lib/pleroma/web/router.ex | 3 - lib/pleroma/web/templates/feed/feed/feed.xml.eex | 1 - lib/pleroma/web/websub/websub.ex | 332 --------------------- .../web/websub/websub_client_subscription.ex | 20 -- lib/pleroma/web/websub/websub_controller.ex | 99 ------ .../web/websub/websub_server_subscription.ex | 17 -- 9 files changed, 1 insertion(+), 498 deletions(-) delete mode 100644 lib/pleroma/web/websub/websub.ex delete mode 100644 lib/pleroma/web/websub/websub_client_subscription.ex delete mode 100644 lib/pleroma/web/websub/websub_controller.ex delete mode 100644 lib/pleroma/web/websub/websub_server_subscription.ex (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d0d3e1f9a..cef011c68 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -28,7 +28,6 @@ defmodule Pleroma.User do alias Pleroma.Web.OAuth alias Pleroma.Web.OStatus alias Pleroma.Web.RelMe - alias Pleroma.Web.Websub alias Pleroma.Workers.BackgroundWorker require Logger @@ -437,12 +436,6 @@ defmodule Pleroma.User do {:error, "Could not follow user: #{followed.nickname} blocked you."} true -> - benchmark? = Pleroma.Config.get([:env]) == :benchmark - - if !followed.local && follower.local && !ap_enabled?(followed) && !benchmark? do - Websub.subscribe(follower, followed) - end - q = from(u in User, where: u.id == ^follower.id, diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b56343beb..2c1ce9c55 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1073,8 +1073,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do Repo.update_all(q, []) - maybe_retire_websub(user.ap_id) - q = from( a in Activity, @@ -1117,19 +1115,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> User.update_and_set_cache() end - def maybe_retire_websub(ap_id) do - # some sanity checks - if is_binary(ap_id) && String.length(ap_id) > 8 do - q = - from( - ws in Pleroma.Web.Websub.WebsubClientSubscription, - where: fragment("? like ?", ws.topic, ^"#{ap_id}%") - ) - - Repo.delete_all(q) - end - end - def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do Map.put(data, "url", url["href"]) end diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 5de1ceef3..3dfc9b580 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -19,7 +19,6 @@ defmodule Pleroma.Web.OStatus do alias Pleroma.Web.OStatus.NoteHandler alias Pleroma.Web.OStatus.UnfollowHandler alias Pleroma.Web.WebFinger - alias Pleroma.Web.Websub def is_representable?(%Activity{} = activity) do object = Object.normalize(activity) @@ -314,11 +313,9 @@ defmodule Pleroma.Web.OStatus do @spec gather_user_info(String.t()) :: {:ok, map()} | {:error, any()} def gather_user_info(username) do - with {:ok, webfinger_data} <- WebFinger.finger(username), - {:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do + with {:ok, webfinger_data} <- WebFinger.finger(username) do data = webfinger_data - |> Map.merge(feed_data) |> Map.put("fqn", username) {:ok, data} diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 80651f3ff..77fe938d5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -509,9 +509,6 @@ defmodule Pleroma.Web.Router do get("/users/:nickname", Feed.FeedController, :feed_redirect) post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming) - post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request) - get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation) - post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming) get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe) end diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex index fbfdc46b5..692cfbd7f 100644 --- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -10,7 +10,6 @@ <%= @user.nickname <> "'s timeline" %> <%= most_recent_update(@activities, @user) %> <%= logo(@user) %> - diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex deleted file mode 100644 index b61f388b8..000000000 --- a/lib/pleroma/web/websub/websub.ex +++ /dev/null @@ -1,332 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Websub do - alias Ecto.Changeset - alias Pleroma.Activity - alias Pleroma.HTTP - alias Pleroma.Instances - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.Endpoint - alias Pleroma.Web.Federator - alias Pleroma.Web.Federator.Publisher - alias Pleroma.Web.OStatus - alias Pleroma.Web.OStatus.FeedRepresenter - alias Pleroma.Web.Router.Helpers - alias Pleroma.Web.Websub.WebsubClientSubscription - alias Pleroma.Web.Websub.WebsubServerSubscription - alias Pleroma.Web.XML - require Logger - - import Ecto.Query - - @behaviour Pleroma.Web.Federator.Publisher - - def verify(subscription, getter \\ &HTTP.get/3) do - challenge = Base.encode16(:crypto.strong_rand_bytes(8)) - lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at) - lease_seconds = lease_seconds |> to_string - - params = %{ - "hub.challenge": challenge, - "hub.lease_seconds": lease_seconds, - "hub.topic": subscription.topic, - "hub.mode": "subscribe" - } - - url = hd(String.split(subscription.callback, "?")) - query = URI.parse(subscription.callback).query || "" - params = Map.merge(params, URI.decode_query(query)) - - with {:ok, response} <- getter.(url, [], params: params), - ^challenge <- response.body do - changeset = Changeset.change(subscription, %{state: "active"}) - Repo.update(changeset) - else - e -> - Logger.debug("Couldn't verify subscription") - Logger.debug(inspect(e)) - {:error, subscription} - end - end - - @supported_activities [ - "Create", - "Follow", - "Like", - "Announce", - "Undo", - "Delete" - ] - - def is_representable?(%Activity{data: %{"type" => type}} = activity) - when type in @supported_activities, - do: Visibility.is_public?(activity) - - def is_representable?(_), do: false - - def publish(topic, user, %{data: %{"type" => type}} = activity) - when type in @supported_activities do - response = - user - |> FeedRepresenter.to_simple_form([activity], [user]) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string - - query = - from( - sub in WebsubServerSubscription, - where: sub.topic == ^topic and sub.state == "active", - where: fragment("? > (NOW() at time zone 'UTC')", sub.valid_until) - ) - - subscriptions = Repo.all(query) - - callbacks = Enum.map(subscriptions, & &1.callback) - reachable_callbacks_metadata = Instances.filter_reachable(callbacks) - reachable_callbacks = Map.keys(reachable_callbacks_metadata) - - subscriptions - |> Enum.filter(&(&1.callback in reachable_callbacks)) - |> Enum.each(fn sub -> - data = %{ - xml: response, - topic: topic, - callback: sub.callback, - secret: sub.secret, - unreachable_since: reachable_callbacks_metadata[sub.callback] - } - - Publisher.enqueue_one(__MODULE__, data) - end) - end - - def publish(_, _, _), do: "" - - def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) - - def sign(secret, doc) do - :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase() - end - - def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do - with {:ok, topic} <- valid_topic(params, user), - {:ok, lease_time} <- lease_time(params), - secret <- params["hub.secret"], - callback <- params["hub.callback"] do - subscription = get_subscription(topic, callback) - - data = %{ - state: subscription.state || "requested", - topic: topic, - secret: secret, - callback: callback - } - - change = Changeset.change(subscription, data) - websub = Repo.insert_or_update!(change) - - change = - Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)}) - - websub = Repo.update!(change) - - Federator.verify_websub(websub) - - {:ok, websub} - else - {:error, reason} -> - Logger.debug("Couldn't create subscription") - Logger.debug(inspect(reason)) - - {:error, reason} - end - end - - def incoming_subscription_request(user, params) do - Logger.info("Unhandled WebSub request for #{user.nickname}: #{inspect(params)}") - - {:error, "Invalid WebSub request"} - end - - defp get_subscription(topic, callback) do - Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) || - %WebsubServerSubscription{} - end - - # Temp hack for mastodon. - defp lease_time(%{"hub.lease_seconds" => ""}) do - # three days - {:ok, 60 * 60 * 24 * 3} - end - - defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do - {:ok, String.to_integer(lease_seconds)} - end - - defp lease_time(_) do - # three days - {:ok, 60 * 60 * 24 * 3} - end - - defp valid_topic(%{"hub.topic" => topic}, user) do - if topic == OStatus.feed_path(user) do - {:ok, OStatus.feed_path(user)} - else - {:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"} - end - end - - def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do - topic = subscribed.info.topic - # FIXME: Race condition, use transactions - {:ok, subscription} = - with subscription when not is_nil(subscription) <- - Repo.get_by(WebsubClientSubscription, topic: topic) do - subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq() - change = Ecto.Changeset.change(subscription, %{subscribers: subscribers}) - Repo.update(change) - else - _e -> - subscription = %WebsubClientSubscription{ - topic: topic, - hub: subscribed.info.hub, - subscribers: [subscriber.ap_id], - state: "requested", - secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(), - user: subscribed - } - - Repo.insert(subscription) - end - - requester.(subscription) - end - - def gather_feed_data(topic, getter \\ &HTTP.get/1) do - with {:ok, response} <- getter.(topic), - status when status in 200..299 <- response.status, - body <- response.body, - doc <- XML.parse_document(body), - uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc), - hub when not is_nil(hub) <- XML.string_from_xpath(~S{/feed/link[@rel="hub"]/@href}, doc) do - name = XML.string_from_xpath("/feed/author[1]/name", doc) - preferred_username = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc) - display_name = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc) - avatar = OStatus.make_avatar_object(doc) - bio = XML.string_from_xpath("/feed/author[1]/summary", doc) - - {:ok, - %{ - "uri" => uri, - "hub" => hub, - "nickname" => preferred_username || name, - "name" => display_name || name, - "host" => URI.parse(uri).host, - "avatar" => avatar, - "bio" => bio - }} - else - e -> - {:error, e} - end - end - - def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do - data = [ - "hub.mode": "subscribe", - "hub.topic": websub.topic, - "hub.secret": websub.secret, - "hub.callback": Helpers.websub_url(Endpoint, :websub_subscription_confirmation, websub.id) - ] - - # This checks once a second if we are confirmed yet - websub_checker = fn -> - helper = fn helper -> - :timer.sleep(1000) - websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted") - if websub, do: websub, else: helper.(helper) - end - - helper.(helper) - end - - task = Task.async(websub_checker) - - with {:ok, %{status: 202}} <- - poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"), - {:ok, websub} <- Task.yield(task, timeout) do - {:ok, websub} - else - e -> - Task.shutdown(task) - - change = Ecto.Changeset.change(websub, %{state: "rejected"}) - {:ok, websub} = Repo.update(change) - - Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end) - Logger.debug(fn -> "error: #{inspect(e)}" end) - - {:error, websub} - end - end - - def refresh_subscriptions(delta \\ 60 * 60 * 24) do - Logger.debug("Refreshing subscriptions") - - cut_off = NaiveDateTime.add(NaiveDateTime.utc_now(), delta) - - query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off) - - subs = Repo.all(query) - - Enum.each(subs, fn sub -> - Federator.request_subscription(sub) - end) - end - - def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} = params) do - signature = sign(secret || "", xml) - Logger.info(fn -> "Pushing #{topic} to #{callback}" end) - - with {:ok, %{status: code}} when code in 200..299 <- - HTTP.post( - callback, - xml, - [ - {"Content-Type", "application/atom+xml"}, - {"X-Hub-Signature", "sha1=#{signature}"} - ] - ) do - if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], - do: Instances.set_reachable(callback) - - Logger.info(fn -> "Pushed to #{callback}, code #{code}" end) - {:ok, code} - else - {_post_result, response} -> - unless params[:unreachable_since], do: Instances.set_reachable(callback) - Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(response)}" end) - {:error, response} - end - end - - def gather_webfinger_links(%User{} = user) do - [ - %{ - "rel" => "http://schemas.google.com/g/2010#updates-from", - "type" => "application/atom+xml", - "href" => OStatus.feed_path(user) - }, - %{ - "rel" => "http://ostatus.org/schema/1.0/subscribe", - "template" => OStatus.remote_follow_path() - } - ] - end - - def gather_nodeinfo_protocol_names, do: ["ostatus"] -end diff --git a/lib/pleroma/web/websub/websub_client_subscription.ex b/lib/pleroma/web/websub/websub_client_subscription.ex deleted file mode 100644 index 23a04b87d..000000000 --- a/lib/pleroma/web/websub/websub_client_subscription.ex +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Websub.WebsubClientSubscription do - use Ecto.Schema - alias Pleroma.User - - schema "websub_client_subscriptions" do - field(:topic, :string) - field(:secret, :string) - field(:valid_until, :naive_datetime_usec) - field(:state, :string) - field(:subscribers, {:array, :string}, default: []) - field(:hub, :string) - belongs_to(:user, User, type: FlakeId.Ecto.CompatType) - - timestamps() - end -end diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex deleted file mode 100644 index 9e8b48b80..000000000 --- a/lib/pleroma/web/websub/websub_controller.ex +++ /dev/null @@ -1,99 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Websub.WebsubController do - use Pleroma.Web, :controller - - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.Federator - alias Pleroma.Web.Websub - alias Pleroma.Web.Websub.WebsubClientSubscription - - require Logger - - plug( - Pleroma.Web.FederatingPlug - when action in [ - :websub_subscription_request, - :websub_subscription_confirmation, - :websub_incoming - ] - ) - - def websub_subscription_request(conn, %{"nickname" => nickname} = params) do - user = User.get_cached_by_nickname(nickname) - - with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do - conn - |> send_resp(202, "Accepted") - else - {:error, reason} -> - conn - |> send_resp(500, reason) - end - end - - # TODO: Extract this into the Websub module - def websub_subscription_confirmation( - conn, - %{ - "id" => id, - "hub.mode" => "subscribe", - "hub.challenge" => challenge, - "hub.topic" => topic - } = params - ) do - Logger.debug("Got WebSub confirmation") - Logger.debug(inspect(params)) - - lease_seconds = - if params["hub.lease_seconds"] do - String.to_integer(params["hub.lease_seconds"]) - else - # Guess 3 days - 60 * 60 * 24 * 3 - end - - with %WebsubClientSubscription{} = websub <- - Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do - valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), lease_seconds) - change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until}) - {:ok, _websub} = Repo.update(change) - - conn - |> send_resp(200, challenge) - else - _e -> - conn - |> send_resp(500, "Error") - end - end - - def websub_subscription_confirmation(conn, params) do - Logger.info("Invalid WebSub confirmation request: #{inspect(params)}") - - conn - |> send_resp(500, "Invalid parameters") - end - - def websub_incoming(conn, %{"id" => id}) do - with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")), - signature <- String.downcase(signature), - %WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id), - {:ok, body, _conn} = read_body(conn), - ^signature <- Websub.sign(websub.secret, body) do - Federator.incoming_doc(body) - - conn - |> send_resp(200, "OK") - else - _e -> - Logger.debug("Can't handle incoming subscription post") - - conn - |> send_resp(500, "Error") - end - end -end diff --git a/lib/pleroma/web/websub/websub_server_subscription.ex b/lib/pleroma/web/websub/websub_server_subscription.ex deleted file mode 100644 index d0ef548da..000000000 --- a/lib/pleroma/web/websub/websub_server_subscription.ex +++ /dev/null @@ -1,17 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Websub.WebsubServerSubscription do - use Ecto.Schema - - schema "websub_server_subscriptions" do - field(:topic, :string) - field(:callback, :string) - field(:secret, :string) - field(:valid_until, :naive_datetime) - field(:state, :string) - - timestamps() - end -end -- cgit v1.2.3 From adb639db56fc40b07edaf9ed8cdf40d6aa2c573b Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:05:45 +0000 Subject: publisher: move remote_users() from Salmon module --- lib/pleroma/web/activity_pub/publisher.ex | 2 +- lib/pleroma/web/federator/publisher.ex | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 3866dacee..2aac4e8b9 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -129,7 +129,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do [] end - Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers ++ fetchers + Pleroma.Web.Federator.Publisher.remote_users(actor, activity) ++ followers ++ fetchers end defp get_cc_ap_ids(ap_id, recipients) do diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 937064638..fb9b26649 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -80,4 +80,30 @@ defmodule Pleroma.Web.Federator.Publisher do links ++ module.gather_nodeinfo_protocol_names() end) end + + @doc """ + Gathers a set of remote users given an IR envelope. + """ + def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do + cc = Map.get(data, "cc", []) + + bcc = + data + |> Map.get("bcc", []) + |> Enum.reduce([], fn ap_id, bcc -> + case Pleroma.List.get_by_ap_id(ap_id) do + %Pleroma.List{user_id: ^user_id} = list -> + {:ok, following} = Pleroma.List.get_following(list) + bcc ++ Enum.map(following, & &1.ap_id) + + _ -> + bcc + end + end) + + [to, cc, bcc] + |> Enum.concat() + |> Enum.map(&User.get_cached_by_ap_id/1) + |> Enum.filter(fn user -> user && !user.local end) + end end -- cgit v1.2.3 From c00ae10af8187cb9d54a0bfbbf37a69a94298703 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:06:53 +0000 Subject: feed: don't advertise salmon endpoint --- lib/pleroma/web/templates/feed/feed/feed.xml.eex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex index 692cfbd7f..45df9dc09 100644 --- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -10,7 +10,6 @@ <%= @user.nickname <> "'s timeline" %> <%= most_recent_update(@activities, @user) %> <%= logo(@user) %> - <%= render @view_module, "_author.xml", assigns %> -- cgit v1.2.3 From a7b92bba68281f3861d1446b85743a0b65fe1115 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:07:20 +0000 Subject: webfinger: stop pulling Salmon data out of WebFinger --- lib/pleroma/web/web_finger/web_finger.ex | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index ecb39ee50..b4cc80179 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -108,7 +108,6 @@ defmodule Pleroma.Web.WebFinger do doc ), subject <- XML.string_from_xpath("//Subject", doc), - salmon <- XML.string_from_xpath(~s{//Link[@rel="salmon"]/@href}, doc), subscribe_address <- XML.string_from_xpath( ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}, @@ -123,7 +122,6 @@ defmodule Pleroma.Web.WebFinger do "magic_key" => magic_key, "topic" => topic, "subject" => subject, - "salmon" => salmon, "subscribe_address" => subscribe_address, "ap_id" => ap_id } @@ -148,16 +146,6 @@ defmodule Pleroma.Web.WebFinger do {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} -> Map.put(data, "ap_id", link["href"]) - {_, "magic-public-key"} -> - "data:application/magic-public-key," <> magic_key = link["href"] - Map.put(data, "magic_key", magic_key) - - {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} -> - Map.put(data, "topic", link["href"]) - - {_, "salmon"} -> - Map.put(data, "salmon", link["href"]) - {_, "http://ostatus.org/schema/1.0/subscribe"} -> Map.put(data, "subscribe_address", link["template"]) -- cgit v1.2.3 From beb9861f9df080cd071c34f37c95e89d1b170138 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:07:54 +0000 Subject: router: disconnect Salmon --- lib/pleroma/web/router.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 77fe938d5..b3b5ada4e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -508,8 +508,6 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/feed", Feed.FeedController, :feed) get("/users/:nickname", Feed.FeedController, :feed_redirect) - post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming) - get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe) end -- cgit v1.2.3 From 835ad5237885cb1c95f678054733b904f20b0bbd Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:09:15 +0000 Subject: remove Salmon module --- lib/pleroma/user/info.ex | 6 - lib/pleroma/web/ostatus/feed_representer.ex | 2 - lib/pleroma/web/ostatus/ostatus.ex | 4 - lib/pleroma/web/ostatus/ostatus_controller.ex | 28 --- lib/pleroma/web/salmon/salmon.ex | 254 -------------------------- 5 files changed, 294 deletions(-) delete mode 100644 lib/pleroma/web/salmon/salmon.ex (limited to 'lib') diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 4b5b43d7f..2d39abcb3 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -39,9 +39,6 @@ defmodule Pleroma.User.Info do field(:settings, :map, default: nil) field(:magic_key, :string, default: nil) field(:uri, :string, default: nil) - field(:topic, :string, default: nil) - field(:hub, :string, default: nil) - field(:salmon, :string, default: nil) field(:hide_followers_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false) field(:hide_followers, :boolean, default: false) @@ -262,9 +259,6 @@ defmodule Pleroma.User.Info do :locked, :magic_key, :uri, - :hub, - :topic, - :salmon, :hide_followers, :hide_follows, :hide_followers_count, diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex index b7b97e505..fa7f7b564 100644 --- a/lib/pleroma/web/ostatus/feed_representer.ex +++ b/lib/pleroma/web/ostatus/feed_representer.ex @@ -40,8 +40,6 @@ defmodule Pleroma.Web.OStatus.FeedRepresenter do {:title, ['#{user.nickname}\'s timeline']}, {:updated, h.(most_recent_update)}, {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]}, - {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []}, - {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []}, {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'], []}, {:author, UserRepresenter.to_simple_form(user)} diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 3dfc9b580..a858759d3 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -37,10 +37,6 @@ defmodule Pleroma.Web.OStatus do def feed_path(user), do: "#{user.ap_id}/feed.atom" - def pubsub_path(user), do: "#{Web.base_url()}/push/hub/#{user.nickname}" - - def salmon_path(user), do: "#{user.ap_id}/salmon" - def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}" def handle_incoming(xml_string, options \\ []) do diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 20f2d9ddc..7466dd8ea 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -24,8 +24,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do {:ap_routes, params: ["uuid"]} when action in [:object, :activity] ) - plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming]) - plug( Pleroma.Plugs.SetFormatPlug when action in [:object, :activity, :notice] @@ -33,32 +31,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do action_fallback(:errors) - defp decode_or_retry(body) do - with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), - {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do - {:ok, doc} - else - _e -> - with [decoded | _] <- Pleroma.Web.Salmon.decode(body), - doc <- XML.parse_document(decoded), - uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc), - {:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true), - {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), - {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do - {:ok, doc} - end - end - end - - def salmon_incoming(conn, _) do - {:ok, body, _conn} = read_body(conn) - {:ok, doc} = decode_or_retry(body) - - Federator.incoming_doc(doc) - - send_resp(conn, 200, "") - end - def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid}) when format in ["json", "activity+json"] do ActivityPubController.call(conn, :object) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex deleted file mode 100644 index 0ffe903cd..000000000 --- a/lib/pleroma/web/salmon/salmon.ex +++ /dev/null @@ -1,254 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Salmon do - @behaviour Pleroma.Web.Federator.Publisher - - use Bitwise - - alias Pleroma.Activity - alias Pleroma.HTTP - alias Pleroma.Instances - alias Pleroma.Keys - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.Federator.Publisher - alias Pleroma.Web.OStatus - alias Pleroma.Web.OStatus.ActivityRepresenter - alias Pleroma.Web.XML - - require Logger - - def decode(salmon) do - doc = XML.parse_document(salmon) - - {:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc) - {:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc) - {:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc) - {:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc) - {:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc) - - {:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace) - {:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace) - alg = to_string(alg) - encoding = to_string(encoding) - type = to_string(type) - - [data, type, encoding, alg, sig] - end - - def fetch_magic_key(salmon) do - with [data, _, _, _, _] <- decode(salmon), - doc <- XML.parse_document(data), - uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc), - {:ok, public_key} <- User.get_public_key_for_ap_id(uri), - magic_key <- encode_key(public_key) do - {:ok, magic_key} - end - end - - def decode_and_validate(magickey, salmon) do - [data, type, encoding, alg, sig] = decode(salmon) - - signed_text = - [data, type, encoding, alg] - |> Enum.map(&Base.url_encode64/1) - |> Enum.join(".") - - key = decode_key(magickey) - - verify = :public_key.verify(signed_text, :sha256, sig, key) - - if verify do - {:ok, data} - else - :error - end - end - - def decode_key("RSA." <> magickey) do - make_integer = fn bin -> - list = :erlang.binary_to_list(bin) - Enum.reduce(list, 0, fn el, acc -> acc <<< 8 ||| el end) - end - - [modulus, exponent] = - magickey - |> String.split(".") - |> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end) - |> Enum.map(make_integer) - - {:RSAPublicKey, modulus, exponent} - end - - def encode_key({:RSAPublicKey, modulus, exponent}) do - modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64() - exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64() - - "RSA.#{modulus_enc}.#{exponent_enc}" - end - - def encode(private_key, doc) do - type = "application/atom+xml" - encoding = "base64url" - alg = "RSA-SHA256" - - signed_text = - [doc, type, encoding, alg] - |> Enum.map(&Base.url_encode64/1) - |> Enum.join(".") - - signature = - signed_text - |> :public_key.sign(:sha256, private_key) - |> to_string - |> Base.url_encode64() - - doc_base64 = - doc - |> Base.url_encode64() - - # Don't need proper xml building, these strings are safe to leave unescaped - salmon = """ - - - #{doc_base64} - #{encoding} - #{alg} - #{signature} - - """ - - {:ok, salmon} - end - - def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do - cc = Map.get(data, "cc", []) - - bcc = - data - |> Map.get("bcc", []) - |> Enum.reduce([], fn ap_id, bcc -> - case Pleroma.List.get_by_ap_id(ap_id) do - %Pleroma.List{user_id: ^user_id} = list -> - {:ok, following} = Pleroma.List.get_following(list) - bcc ++ Enum.map(following, & &1.ap_id) - - _ -> - bcc - end - end) - - [to, cc, bcc] - |> Enum.concat() - |> Enum.map(&User.get_cached_by_ap_id/1) - |> Enum.filter(fn user -> user && !user.local end) - end - - @doc "Pushes an activity to remote account." - def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params), - do: publish_one(Map.put(params, :recipient, salmon)) - - def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do - with {:ok, %{status: code}} when code in 200..299 <- - HTTP.post( - url, - feed, - [{"Content-Type", "application/magic-envelope+xml"}] - ) do - if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], - do: Instances.set_reachable(url) - - Logger.debug(fn -> "Pushed to #{url}, code #{code}" end) - {:ok, code} - else - e -> - unless params[:unreachable_since], do: Instances.set_reachable(url) - Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end) - {:error, "Unreachable instance"} - end - end - - def publish_one(%{recipient_id: recipient_id} = params) do - recipient = User.get_cached_by_id(recipient_id) - - params - |> Map.delete(:recipient_id) - |> Map.put(:recipient, recipient) - |> publish_one() - end - - def publish_one(_), do: :noop - - @supported_activities [ - "Create", - "Follow", - "Like", - "Announce", - "Undo", - "Delete" - ] - - def is_representable?(%Activity{data: %{"type" => type}} = activity) - when type in @supported_activities, - do: Visibility.is_public?(activity) - - def is_representable?(_), do: false - - @doc """ - Publishes an activity to remote accounts - """ - @spec publish(User.t(), Pleroma.Activity.t()) :: none - def publish(user, activity) - - def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity) - when type in @supported_activities do - feed = ActivityRepresenter.to_simple_form(activity, user, true) - - if feed do - feed = - ActivityRepresenter.wrap_with_entry(feed) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string - - {:ok, private, _} = Keys.keys_from_pem(keys) - {:ok, feed} = encode(private, feed) - - remote_users = remote_users(user, activity) - - salmon_urls = Enum.map(remote_users, & &1.info.salmon) - reachable_urls_metadata = Instances.filter_reachable(salmon_urls) - reachable_urls = Map.keys(reachable_urls_metadata) - - remote_users - |> Enum.filter(&(&1.info.salmon in reachable_urls)) - |> Enum.each(fn remote_user -> - Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) - - Publisher.enqueue_one(__MODULE__, %{ - recipient_id: remote_user.id, - feed: feed, - unreachable_since: reachable_urls_metadata[remote_user.info.salmon] - }) - end) - end - end - - def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) - - def gather_webfinger_links(%User{} = user) do - {:ok, _private, public} = Keys.keys_from_pem(user.keys) - magic_key = encode_key(public) - - [ - %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, - %{ - "rel" => "magic-public-key", - "href" => "data:application/magic-public-key,#{magic_key}" - } - ] - end - - def gather_nodeinfo_protocol_names, do: [] -end -- cgit v1.2.3 From 6a1f4c5145c05efdfa1b0d56ba25bf87e51c7f82 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:15:09 +0000 Subject: federator: remove OStatus incoming document support --- lib/pleroma/web/federator/federator.ex | 9 --------- lib/pleroma/workers/receiver_worker.ex | 4 ---- 2 files changed, 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 8227d1a3a..8c83f9aea 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -30,10 +30,6 @@ defmodule Pleroma.Web.Federator do # Client API - def incoming_doc(doc) do - ReceiverWorker.enqueue("incoming_doc", %{"body" => doc}) - end - def incoming_ap_doc(params) do ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}) end @@ -62,11 +58,6 @@ defmodule Pleroma.Web.Federator do end end - def perform(:incoming_doc, doc) do - Logger.info("Got document, trying to parse") - OStatus.handle_incoming(doc) - end - def perform(:incoming_ap_doc, params) do Logger.info("Handling incoming AP activity") diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 83d528a66..8ad756b62 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -8,10 +8,6 @@ defmodule Pleroma.Workers.ReceiverWorker do use Pleroma.Workers.WorkerHelper, queue: "federator_incoming" @impl Oban.Worker - def perform(%{"op" => "incoming_doc", "body" => doc}, _job) do - Federator.perform(:incoming_doc, doc) - end - def perform(%{"op" => "incoming_ap_doc", "params" => params}, _job) do Federator.perform(:incoming_ap_doc, params) end -- cgit v1.2.3 From d379b4876927701f0fa9e4886f9fd552fe71d9c9 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 17 Oct 2019 23:37:21 +0000 Subject: kill almost all of the OStatus module --- lib/pleroma/object/fetcher.ex | 12 +- lib/pleroma/user.ex | 26 +- lib/pleroma/web/federator/federator.ex | 1 - lib/pleroma/web/ostatus/activity_representer.ex | 313 ----------------- lib/pleroma/web/ostatus/feed_representer.ex | 64 ---- lib/pleroma/web/ostatus/handlers/delete_handler.ex | 18 - lib/pleroma/web/ostatus/handlers/follow_handler.ex | 26 -- lib/pleroma/web/ostatus/handlers/note_handler.ex | 168 --------- .../web/ostatus/handlers/unfollow_handler.ex | 22 -- lib/pleroma/web/ostatus/ostatus.ex | 388 --------------------- lib/pleroma/web/ostatus/ostatus_controller.ex | 18 +- lib/pleroma/web/ostatus/user_representer.ex | 41 --- 12 files changed, 5 insertions(+), 1092 deletions(-) delete mode 100644 lib/pleroma/web/ostatus/activity_representer.ex delete mode 100644 lib/pleroma/web/ostatus/feed_representer.ex delete mode 100644 lib/pleroma/web/ostatus/handlers/delete_handler.ex delete mode 100644 lib/pleroma/web/ostatus/handlers/follow_handler.ex delete mode 100644 lib/pleroma/web/ostatus/handlers/note_handler.ex delete mode 100644 lib/pleroma/web/ostatus/handlers/unfollow_handler.ex delete mode 100644 lib/pleroma/web/ostatus/ostatus.ex delete mode 100644 lib/pleroma/web/ostatus/user_representer.ex (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 5e064fd87..9436e2730 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Object.Fetcher do alias Pleroma.Signature alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.OStatus require Logger require Pleroma.Constants @@ -87,15 +86,8 @@ defmodule Pleroma.Object.Fetcher do {:fetch_object, %Object{} = object} -> {:ok, object} - _e -> - # Only fallback when receiving a fetch/normalization error with ActivityPub - Logger.info("Couldn't get object via AP, trying out OStatus fetching...") - - # FIXME: OStatus Object Containment? - case OStatus.fetch_activity_from_url(id) do - {:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)} - e -> e - end + e -> + e end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index cef011c68..ec705b8f6 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -26,7 +26,6 @@ defmodule Pleroma.User do alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils alias Pleroma.Web.OAuth - alias Pleroma.Web.OStatus alias Pleroma.Web.RelMe alias Pleroma.Workers.BackgroundWorker @@ -609,12 +608,7 @@ defmodule Pleroma.User do Cachex.fetch!(:user_cache, key, fn -> user_info(user) end) end - def fetch_by_nickname(nickname) do - case ActivityPub.make_user_from_nickname(nickname) do - {:ok, user} -> {:ok, user} - _ -> OStatus.make_user(nickname) - end - end + def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname) def get_or_fetch_by_nickname(nickname) do with %User{} = user <- get_by_nickname(nickname) do @@ -1241,18 +1235,7 @@ defmodule Pleroma.User do def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy]) - def fetch_by_ap_id(ap_id) do - case ActivityPub.make_user_from_ap_id(ap_id) do - {:ok, user} -> - {:ok, user} - - _ -> - case OStatus.make_user(ap_id) do - {:ok, user} -> {:ok, user} - _ -> {:error, "Could not fetch by AP id"} - end - end - end + def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id) def get_or_fetch_by_ap_id(ap_id) do user = get_cached_by_ap_id(ap_id) @@ -1307,11 +1290,6 @@ defmodule Pleroma.User do {:ok, key} end - # OStatus Magic Key - def public_key_from_info(%{magic_key: magic_key}) when not is_nil(magic_key) do - {:ok, Pleroma.Web.Salmon.decode_key(magic_key)} - end - def public_key_from_info(_), do: {:error, "not found key"} def get_public_key_for_ap_id(ap_id) do diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 8c83f9aea..e8a56ebd7 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Federator.Publisher - alias Pleroma.Web.OStatus alias Pleroma.Workers.PublisherWorker alias Pleroma.Workers.ReceiverWorker diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex deleted file mode 100644 index 8e55b9f0b..000000000 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ /dev/null @@ -1,313 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.ActivityRepresenter do - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.OStatus.UserRepresenter - - require Logger - require Pleroma.Constants - - defp get_href(id) do - with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do - external_url - else - _e -> id - end - end - - defp get_in_reply_to(activity) do - with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do - [ - {:"thr:in-reply-to", - [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []} - ] - else - _ -> - [] - end - end - - defp get_mentions(to) do - Enum.map(to, fn id -> - cond do - # Special handling for the AP/Ostatus public collections - Pleroma.Constants.as_public() == id -> - {:link, - [ - rel: "mentioned", - "ostatus:object-type": "http://activitystrea.ms/schema/1.0/collection", - href: "http://activityschema.org/collection/public" - ], []} - - # Ostatus doesn't handle follower collections, ignore these. - Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) -> - [] - - true -> - {:link, - [ - rel: "mentioned", - "ostatus:object-type": "http://activitystrea.ms/schema/1.0/person", - href: id - ], []} - end - end) - end - - defp get_links(%{local: true}, %{"id" => object_id}) do - h = fn str -> [to_charlist(str)] end - - [ - {:link, [type: ['application/atom+xml'], href: h.(object_id), rel: 'self'], []}, - {:link, [type: ['text/html'], href: h.(object_id), rel: 'alternate'], []} - ] - end - - defp get_links(%{local: false}, %{"external_url" => external_url}) do - h = fn str -> [to_charlist(str)] end - - [ - {:link, [type: ['text/html'], href: h.(external_url), rel: 'alternate'], []} - ] - end - - defp get_links(_activity, _object_data), do: [] - - defp get_emoji_links(emojis) do - Enum.map(emojis, fn {emoji, file} -> - {:link, [name: to_charlist(emoji), rel: 'emoji', href: to_charlist(file)], []} - end) - end - - def to_simple_form(activity, user, with_author \\ false) - - def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do - h = fn str -> [to_charlist(str)] end - - object = Object.normalize(activity) - - updated_at = object.data["published"] - inserted_at = object.data["published"] - - attachments = - Enum.map(object.data["attachment"] || [], fn attachment -> - url = hd(attachment["url"]) - - {:link, - [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])], - []} - end) - - in_reply_to = get_in_reply_to(activity) - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - mentions = activity.recipients |> get_mentions - - categories = - (object.data["tag"] || []) - |> Enum.map(fn tag -> - if is_binary(tag) do - {:category, [term: to_charlist(tag)], []} - else - nil - end - end) - |> Enum.filter(& &1) - - emoji_links = get_emoji_links(object.data["emoji"] || %{}) - - summary = - if object.data["summary"] do - [{:summary, [], h.(object.data["summary"])}] - else - [] - end - - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']}, - # For notes, federate the object id. - {:id, h.(object.data["id"])}, - {:title, ['New note by #{user.nickname}']}, - {:content, [type: 'html'], h.(object.data["content"] |> String.replace(~r/[\n\r]/, ""))}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"ostatus:conversation", [ref: h.(activity.data["context"])], - h.(activity.data["context"])}, - {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []} - ] ++ - summary ++ - get_links(activity, object.data) ++ - categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links - end - - def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) do - h = fn str -> [to_charlist(str)] end - - updated_at = activity.data["published"] - inserted_at = activity.data["published"] - - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - mentions = activity.recipients |> get_mentions - - [ - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/favorite']}, - {:id, h.(activity.data["id"])}, - {:title, ['New favorite by #{user.nickname}']}, - {:content, [type: 'html'], ['#{user.nickname} favorited something']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"activity:object", - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, - # For notes, federate the object id. - {:id, h.(activity.data["object"])} - ]}, - {:"ostatus:conversation", [ref: h.(activity.data["context"])], - h.(activity.data["context"])}, - {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, - {:"thr:in-reply-to", [ref: to_charlist(activity.data["object"])], []} - ] ++ author ++ mentions - end - - def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_author) do - h = fn str -> [to_charlist(str)] end - - updated_at = activity.data["published"] - inserted_at = activity.data["published"] - - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - - retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) - retweeted_object = Object.normalize(retweeted_activity) - retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"]) - - retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true) - - mentions = - ([retweeted_user.ap_id] ++ activity.recipients) - |> Enum.uniq() - |> get_mentions() - - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']}, - {:id, h.(activity.data["id"])}, - {:title, ['#{user.nickname} repeated a notice']}, - {:content, [type: 'html'], ['RT #{retweeted_object.data["content"]}']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"ostatus:conversation", [ref: h.(activity.data["context"])], - h.(activity.data["context"])}, - {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, - {:"activity:object", retweeted_xml} - ] ++ mentions ++ author - end - - def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) do - h = fn str -> [to_charlist(str)] end - - updated_at = activity.data["published"] - inserted_at = activity.data["published"] - - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - - mentions = (activity.recipients || []) |> get_mentions - - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']}, - {:id, h.(activity.data["id"])}, - {:title, ['#{user.nickname} started following #{activity.data["object"]}']}, - {:content, [type: 'html'], - ['#{user.nickname} started following #{activity.data["object"]}']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"activity:object", - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, - {:id, h.(activity.data["object"])}, - {:uri, h.(activity.data["object"])} - ]}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} - ] ++ mentions ++ author - end - - # Only undos of follow for now. Will need to get redone once there are more - def to_simple_form( - %{data: %{"type" => "Undo", "object" => %{"type" => "Follow"} = follow_activity}} = - activity, - user, - with_author - ) do - h = fn str -> [to_charlist(str)] end - - updated_at = activity.data["published"] - inserted_at = activity.data["published"] - - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - - mentions = (activity.recipients || []) |> get_mentions - follow_activity = Activity.normalize(follow_activity) - - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']}, - {:id, h.(activity.data["id"])}, - {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, - {:content, [type: 'html'], - ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"activity:object", - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, - {:id, h.(follow_activity.data["object"])}, - {:uri, h.(follow_activity.data["object"])} - ]}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} - ] ++ mentions ++ author - end - - def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do - h = fn str -> [to_charlist(str)] end - - updated_at = activity.data["published"] - inserted_at = activity.data["published"] - - author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/delete']}, - {:id, h.(activity.data["object"])}, - {:title, ['An object was deleted']}, - {:content, [type: 'html'], ['An object was deleted']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)} - ] ++ author - end - - def to_simple_form(_, _, _), do: nil - - def wrap_with_entry(simple_form) do - [ - { - :entry, - [ - xmlns: 'http://www.w3.org/2005/Atom', - "xmlns:thr": 'http://purl.org/syndication/thread/1.0', - "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', - "xmlns:poco": 'http://portablecontacts.net/spec/1.0', - "xmlns:ostatus": 'http://ostatus.org/schema/1.0' - ], - simple_form - } - ] - end -end diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex deleted file mode 100644 index fa7f7b564..000000000 --- a/lib/pleroma/web/ostatus/feed_representer.ex +++ /dev/null @@ -1,64 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.FeedRepresenter do - alias Pleroma.User - alias Pleroma.Web.MediaProxy - alias Pleroma.Web.OStatus - alias Pleroma.Web.OStatus.ActivityRepresenter - alias Pleroma.Web.OStatus.UserRepresenter - - def to_simple_form(user, activities, _users) do - most_recent_update = - (List.first(activities) || user).updated_at - |> NaiveDateTime.to_iso8601() - - h = fn str -> [to_charlist(str)] end - - last_activity = List.last(activities) - - entries = - activities - |> Enum.map(fn activity -> - {:entry, ActivityRepresenter.to_simple_form(activity, user)} - end) - |> Enum.filter(fn {_, form} -> form end) - - [ - { - :feed, - [ - xmlns: 'http://www.w3.org/2005/Atom', - "xmlns:thr": 'http://purl.org/syndication/thread/1.0', - "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', - "xmlns:poco": 'http://portablecontacts.net/spec/1.0', - "xmlns:ostatus": 'http://ostatus.org/schema/1.0' - ], - [ - {:id, h.(OStatus.feed_path(user))}, - {:title, ['#{user.nickname}\'s timeline']}, - {:updated, h.(most_recent_update)}, - {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]}, - {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'], - []}, - {:author, UserRepresenter.to_simple_form(user)} - ] ++ - if last_activity do - [ - {:link, - [ - rel: 'next', - href: - to_charlist(OStatus.feed_path(user)) ++ - '?max_id=' ++ to_charlist(last_activity.id), - type: 'application/atom+xml' - ], []} - ] - else - [] - end ++ entries - } - ] - end -end diff --git a/lib/pleroma/web/ostatus/handlers/delete_handler.ex b/lib/pleroma/web/ostatus/handlers/delete_handler.ex deleted file mode 100644 index ac2dc115c..000000000 --- a/lib/pleroma/web/ostatus/handlers/delete_handler.ex +++ /dev/null @@ -1,18 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.DeleteHandler do - require Logger - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.XML - - def handle_delete(entry, _doc \\ nil) do - with id <- XML.string_from_xpath("//id", entry), - %Object{} = object <- Object.normalize(id), - {:ok, delete} <- ActivityPub.delete(object, local: false) do - delete - end - end -end diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex deleted file mode 100644 index 24513972e..000000000 --- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex +++ /dev/null @@ -1,26 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.FollowHandler do - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.OStatus - alias Pleroma.Web.XML - - def handle(entry, doc) do - with {:ok, actor} <- OStatus.find_make_or_update_actor(doc), - id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), - followed_uri when not is_nil(followed_uri) <- - XML.string_from_xpath("/entry/activity:object/id", entry), - {:ok, followed} <- OStatus.find_or_make_user(followed_uri), - {:locked, false} <- {:locked, followed.info.locked}, - {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do - User.follow(actor, followed) - {:ok, activity} - else - {:locked, true} -> - {:error, "It's not possible to follow locked accounts over OStatus"} - end - end -end diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex deleted file mode 100644 index 7fae14f7b..000000000 --- a/lib/pleroma/web/ostatus/handlers/note_handler.ex +++ /dev/null @@ -1,168 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.NoteHandler do - require Logger - require Pleroma.Constants - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Federator - alias Pleroma.Web.OStatus - alias Pleroma.Web.XML - - @doc """ - Get the context for this note. Uses this: - 1. The context of the parent activity - 2. The conversation reference in the ostatus xml - 3. A newly generated context id. - """ - def get_context(entry, in_reply_to) do - context = - (XML.string_from_xpath("//ostatus:conversation[1]", entry) || - XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "") - |> String.trim() - - with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do - context - else - _e -> - if String.length(context) > 0 do - context - else - Utils.generate_context_id() - end - end - end - - def get_people_mentions(entry) do - :xmerl_xpath.string( - '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]', - entry - ) - |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end) - end - - def get_collection_mentions(entry) do - transmogrify = fn - "http://activityschema.org/collection/public" -> - Pleroma.Constants.as_public() - - group -> - group - end - - :xmerl_xpath.string( - '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]', - entry - ) - |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end) - end - - def get_mentions(entry) do - (get_people_mentions(entry) ++ get_collection_mentions(entry)) - |> Enum.filter(& &1) - end - - def get_emoji(entry) do - try do - :xmerl_xpath.string('//link[@rel="emoji"]', entry) - |> Enum.reduce(%{}, fn emoji, acc -> - Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji)) - end) - rescue - _e -> nil - end - end - - def make_to_list(actor, mentions) do - [ - actor.follower_address - ] ++ mentions - end - - def add_external_url(note, entry) do - url = XML.string_from_xpath("//link[@rel='alternate' and @type='text/html']/@href", entry) - Map.put(note, "external_url", url) - end - - def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do - with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do - activity - else - _e -> - with true <- Federator.allowed_incoming_reply_depth?(options[:depth]), - in_reply_to_href when not is_nil(in_reply_to_href) <- - XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry), - {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do - activity - else - _e -> nil - end - end - end - - # TODO: Clean this up a bit. - def handle_note(entry, doc \\ nil, options \\ []) do - with id <- XML.string_from_xpath("//id", entry), - activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id), - [author] <- :xmerl_xpath.string('//author[1]', doc), - {:ok, actor} <- OStatus.find_make_or_update_actor(author), - content_html <- OStatus.get_content(entry), - cw <- OStatus.get_cw(entry), - in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry), - options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1), - in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options), - in_reply_to_object <- - (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil, - in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to, - attachments <- OStatus.get_attachments(entry), - context <- get_context(entry, in_reply_to), - tags <- OStatus.get_tags(entry), - mentions <- get_mentions(entry), - to <- make_to_list(actor, mentions), - date <- XML.string_from_xpath("//published", entry), - unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted", - cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []), - note <- - CommonAPI.Utils.make_note_data( - actor.ap_id, - to, - context, - content_html, - attachments, - in_reply_to_activity, - [], - cw - ), - note <- note |> Map.put("id", id) |> Map.put("tag", tags), - note <- note |> Map.put("published", date), - note <- note |> Map.put("emoji", get_emoji(entry)), - note <- add_external_url(note, entry), - note <- note |> Map.put("cc", cc), - # TODO: Handle this case in make_note_data - note <- - if( - in_reply_to && !in_reply_to_activity, - do: note |> Map.put("inReplyTo", in_reply_to), - else: note - ) do - ActivityPub.create(%{ - to: to, - actor: actor, - context: context, - object: note, - published: date, - local: false, - additional: %{"cc" => cc} - }) - else - %Activity{} = activity -> {:ok, activity} - e -> {:error, e} - end - end -end diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex deleted file mode 100644 index 2062432e3..000000000 --- a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex +++ /dev/null @@ -1,22 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.UnfollowHandler do - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.OStatus - alias Pleroma.Web.XML - - def handle(entry, doc) do - with {:ok, actor} <- OStatus.find_make_or_update_actor(doc), - id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), - followed_uri when not is_nil(followed_uri) <- - XML.string_from_xpath("/entry/activity:object/id", entry), - {:ok, followed} <- OStatus.find_or_make_user(followed_uri), - {:ok, activity} <- ActivityPub.unfollow(actor, followed, id, false) do - User.unfollow(actor, followed) - {:ok, activity} - end - end -end diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex deleted file mode 100644 index a858759d3..000000000 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ /dev/null @@ -1,388 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus do - import Pleroma.Web.XML - require Logger - - alias Pleroma.Activity - alias Pleroma.HTTP - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.OStatus.DeleteHandler - alias Pleroma.Web.OStatus.FollowHandler - alias Pleroma.Web.OStatus.NoteHandler - alias Pleroma.Web.OStatus.UnfollowHandler - alias Pleroma.Web.WebFinger - - def is_representable?(%Activity{} = activity) do - object = Object.normalize(activity) - - cond do - is_nil(object) -> - false - - Visibility.is_public?(activity) && object.data["type"] == "Note" -> - true - - true -> - false - end - end - - def feed_path(user), do: "#{user.ap_id}/feed.atom" - - def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}" - - def handle_incoming(xml_string, options \\ []) do - with doc when doc != :error <- parse_document(xml_string) do - with {:ok, actor_user} <- find_make_or_update_actor(doc), - do: Pleroma.Instances.set_reachable(actor_user.ap_id) - - entries = :xmerl_xpath.string('//entry', doc) - - activities = - Enum.map(entries, fn entry -> - {:xmlObj, :string, object_type} = - :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry) - - {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry) - Logger.debug("Handling #{verb}") - - try do - case verb do - 'http://activitystrea.ms/schema/1.0/delete' -> - with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity - - 'http://activitystrea.ms/schema/1.0/follow' -> - with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity - - 'http://activitystrea.ms/schema/1.0/unfollow' -> - with {:ok, activity} <- UnfollowHandler.handle(entry, doc), do: activity - - 'http://activitystrea.ms/schema/1.0/share' -> - with {:ok, activity, retweeted_activity} <- handle_share(entry, doc), - do: [activity, retweeted_activity] - - 'http://activitystrea.ms/schema/1.0/favorite' -> - with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc), - do: [activity, favorited_activity] - - _ -> - case object_type do - 'http://activitystrea.ms/schema/1.0/note' -> - with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options), - do: activity - - 'http://activitystrea.ms/schema/1.0/comment' -> - with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options), - do: activity - - _ -> - Logger.error("Couldn't parse incoming document") - nil - end - end - rescue - e -> - Logger.error("Error occured while handling activity") - Logger.error(xml_string) - Logger.error(inspect(e)) - nil - end - end) - |> Enum.filter(& &1) - - {:ok, activities} - else - _e -> {:error, []} - end - end - - def make_share(entry, doc, retweeted_activity) do - with {:ok, actor} <- find_make_or_update_actor(doc), - %Object{} = object <- Object.normalize(retweeted_activity), - id when not is_nil(id) <- string_from_xpath("/entry/id", entry), - {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do - {:ok, activity} - end - end - - def handle_share(entry, doc) do - with {:ok, retweeted_activity} <- get_or_build_object(entry), - {:ok, activity} <- make_share(entry, doc, retweeted_activity) do - {:ok, activity, retweeted_activity} - else - e -> {:error, e} - end - end - - def make_favorite(entry, doc, favorited_activity) do - with {:ok, actor} <- find_make_or_update_actor(doc), - %Object{} = object <- Object.normalize(favorited_activity), - id when not is_nil(id) <- string_from_xpath("/entry/id", entry), - {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do - {:ok, activity} - end - end - - def get_or_build_object(entry) do - with {:ok, activity} <- get_or_try_fetching(entry) do - {:ok, activity} - else - _e -> - with [object] <- :xmerl_xpath.string('/entry/activity:object', entry) do - NoteHandler.handle_note(object, object) - end - end - end - - def get_or_try_fetching(entry) do - Logger.debug("Trying to get entry from db") - - with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry), - %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do - {:ok, activity} - else - _ -> - Logger.debug("Couldn't get, will try to fetch") - - with href when not is_nil(href) <- - string_from_xpath("//activity:object[1]/link[@type=\"text/html\"]/@href", entry), - {:ok, [favorited_activity]} <- fetch_activity_from_url(href) do - {:ok, favorited_activity} - else - e -> Logger.debug("Couldn't find href: #{inspect(e)}") - end - end - end - - def handle_favorite(entry, doc) do - with {:ok, favorited_activity} <- get_or_try_fetching(entry), - {:ok, activity} <- make_favorite(entry, doc, favorited_activity) do - {:ok, activity, favorited_activity} - else - e -> {:error, e} - end - end - - def get_attachments(entry) do - :xmerl_xpath.string('/entry/link[@rel="enclosure"]', entry) - |> Enum.map(fn enclosure -> - with href when not is_nil(href) <- string_from_xpath("/link/@href", enclosure), - type when not is_nil(type) <- string_from_xpath("/link/@type", enclosure) do - %{ - "type" => "Attachment", - "url" => [ - %{ - "type" => "Link", - "mediaType" => type, - "href" => href - } - ] - } - end - end) - |> Enum.filter(& &1) - end - - @doc """ - Gets the content from a an entry. - """ - def get_content(entry) do - string_from_xpath("//content", entry) - end - - @doc """ - Get the cw that mastodon uses. - """ - def get_cw(entry) do - case string_from_xpath("/*/summary", entry) do - cw when not is_nil(cw) -> cw - _ -> nil - end - end - - def get_tags(entry) do - :xmerl_xpath.string('//category', entry) - |> Enum.map(fn category -> string_from_xpath("/category/@term", category) end) - |> Enum.filter(& &1) - |> Enum.map(&String.downcase/1) - end - - def maybe_update(doc, user) do - case string_from_xpath("//author[1]/ap_enabled", doc) do - "true" -> - Transmogrifier.upgrade_user_from_ap_id(user.ap_id) - - _ -> - maybe_update_ostatus(doc, user) - end - end - - def maybe_update_ostatus(doc, user) do - old_data = Map.take(user, [:bio, :avatar, :name]) - - with false <- user.local, - avatar <- make_avatar_object(doc), - bio <- string_from_xpath("//author[1]/summary", doc), - name <- string_from_xpath("//author[1]/poco:displayName", doc), - new_data <- %{ - avatar: avatar || old_data.avatar, - name: name || old_data.name, - bio: bio || old_data.bio - }, - false <- new_data == old_data do - change = Ecto.Changeset.change(user, new_data) - User.update_and_set_cache(change) - else - _ -> - {:ok, user} - end - end - - def find_make_or_update_actor(doc) do - uri = string_from_xpath("//author/uri[1]", doc) - - with {:ok, %User{} = user} <- find_or_make_user(uri), - {:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do - maybe_update(doc, user) - else - {:ap_enabled, true} -> - {:error, :invalid_protocol} - - _ -> - {:error, :unknown_user} - end - end - - @spec find_or_make_user(String.t()) :: {:ok, User.t()} - def find_or_make_user(uri) do - case User.get_by_ap_id(uri) do - %User{} = user -> {:ok, user} - _ -> make_user(uri) - end - end - - @spec make_user(String.t(), boolean()) :: {:ok, User.t()} | {:error, any()} - def make_user(uri, update \\ false) do - with {:ok, info} <- gather_user_info(uri) do - with false <- update, - %User{} = user <- User.get_cached_by_ap_id(info["uri"]) do - {:ok, user} - else - _e -> User.insert_or_update_user(build_user_data(info)) - end - end - end - - defp build_user_data(info) do - %{ - name: info["name"], - nickname: info["nickname"] <> "@" <> info["host"], - ap_id: info["uri"], - info: info, - avatar: info["avatar"], - bio: info["bio"] - } - end - - # TODO: Just takes the first one for now. - def make_avatar_object(author_doc, rel \\ "avatar") do - href = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@href", author_doc) - type = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@type", author_doc) - - if href do - %{ - "type" => "Image", - "url" => [%{"type" => "Link", "mediaType" => type, "href" => href}] - } - else - nil - end - end - - @spec gather_user_info(String.t()) :: {:ok, map()} | {:error, any()} - def gather_user_info(username) do - with {:ok, webfinger_data} <- WebFinger.finger(username) do - data = - webfinger_data - |> Map.put("fqn", username) - - {:ok, data} - else - e -> - Logger.debug(fn -> "Couldn't gather info for #{username}" end) - {:error, e} - end - end - - # Regex-based 'parsing' so we don't have to pull in a full html parser - # It's a hack anyway. Maybe revisit this in the future - @mastodon_regex ~r// - @gs_regex ~r// - @gs_classic_regex ~r// - def get_atom_url(body) do - cond do - Regex.match?(@mastodon_regex, body) -> - [[_, match]] = Regex.scan(@mastodon_regex, body) - {:ok, match} - - Regex.match?(@gs_regex, body) -> - [[_, match]] = Regex.scan(@gs_regex, body) - {:ok, match} - - Regex.match?(@gs_classic_regex, body) -> - [[_, match]] = Regex.scan(@gs_classic_regex, body) - {:ok, match} - - true -> - Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end) - {:error, "Couldn't find the Atom link"} - end - end - - def fetch_activity_from_atom_url(url, options \\ []) do - with true <- String.starts_with?(url, "http"), - {:ok, %{body: body, status: code}} when code in 200..299 <- - HTTP.get(url, [{:Accept, "application/atom+xml"}]) do - Logger.debug("Got document from #{url}, handling...") - handle_incoming(body, options) - else - e -> - Logger.debug("Couldn't get #{url}: #{inspect(e)}") - e - end - end - - def fetch_activity_from_html_url(url, options \\ []) do - Logger.debug("Trying to fetch #{url}") - - with true <- String.starts_with?(url, "http"), - {:ok, %{body: body}} <- HTTP.get(url, []), - {:ok, atom_url} <- get_atom_url(body) do - fetch_activity_from_atom_url(atom_url, options) - else - e -> - Logger.debug("Couldn't get #{url}: #{inspect(e)}") - e - end - end - - def fetch_activity_from_url(url, options \\ []) do - with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url, options) do - {:ok, activities} - else - _e -> fetch_activity_from_html_url(url, options) - end - rescue - e -> - Logger.debug("Couldn't get #{url}: #{inspect(e)}") - {:error, "Couldn't get #{url}: #{inspect(e)}"} - end -end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 7466dd8ea..6958519de 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -13,11 +13,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint - alias Pleroma.Web.Federator alias Pleroma.Web.Metadata.PlayerView - alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.Router - alias Pleroma.Web.XML plug( Pleroma.Plugs.RateLimiter, @@ -151,23 +148,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do |> render("object.json", %{object: object}) end - defp represent_activity(_conn, "activity+json", _, _) do + defp represent_activity(_conn, _, _, _) do {:error, :not_found} end - defp represent_activity(conn, _, activity, user) do - response = - activity - |> ActivityRepresenter.to_simple_form(user, true) - |> ActivityRepresenter.wrap_with_entry() - |> :xmerl.export_simple(:xmerl_xml) - |> to_string - - conn - |> put_resp_content_type("application/atom+xml") - |> send_resp(200, response) - end - def errors(conn, {:error, :not_found}) do render_error(conn, :not_found, "Not found") end diff --git a/lib/pleroma/web/ostatus/user_representer.ex b/lib/pleroma/web/ostatus/user_representer.ex deleted file mode 100644 index 852be6eb4..000000000 --- a/lib/pleroma/web/ostatus/user_representer.ex +++ /dev/null @@ -1,41 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.UserRepresenter do - alias Pleroma.User - - def to_simple_form(user) do - ap_id = to_charlist(user.ap_id) - nickname = to_charlist(user.nickname) - name = to_charlist(user.name) - bio = to_charlist(user.bio) - avatar_url = to_charlist(User.avatar_url(user)) - - banner = - if banner_url = User.banner_url(user) do - [{:link, [rel: 'header', href: banner_url], []}] - else - [] - end - - ap_enabled = - if user.local do - [{:ap_enabled, ['true']}] - else - [] - end - - [ - {:id, [ap_id]}, - {:"activity:object", ['http://activitystrea.ms/schema/1.0/person']}, - {:uri, [ap_id]}, - {:"poco:preferredUsername", [nickname]}, - {:"poco:displayName", [name]}, - {:"poco:note", [bio]}, - {:summary, [bio]}, - {:name, [nickname]}, - {:link, [rel: 'avatar', href: avatar_url], []} - ] ++ banner ++ ap_enabled - end -end -- cgit v1.2.3 From 48059c03c91b2437779ac42581812c07530c1a34 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 00:30:01 +0000 Subject: fix up some tests --- lib/pleroma/object/fetcher.ex | 4 ++-- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 9436e2730..8975fb47e 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -162,8 +162,8 @@ defmodule Pleroma.Object.Fetcher do {:ok, %{status: code}} when code in [404, 410] -> {:error, "Object has been deleted"} - e -> - {:error, e} + _ -> + {:error, "Could not fetch by AP id"} end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d391732a2..d631e43c6 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1219,7 +1219,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do data <- maybe_update_follow_information(data) do {:ok, data} else - e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") + e -> + Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") + {:error, e} end end -- cgit v1.2.3 From 6f110fc04ca31bf571fb80a2a852ab60ddd496ff Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 02:42:25 +0000 Subject: object fetcher: fix up error handling --- lib/pleroma/object/fetcher.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 8975fb47e..9436e2730 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -162,8 +162,8 @@ defmodule Pleroma.Object.Fetcher do {:ok, %{status: code}} when code in [404, 410] -> {:error, "Object has been deleted"} - _ -> - {:error, "Could not fetch by AP id"} + e -> + {:error, e} end end -- cgit v1.2.3 From bf2107743ffcd3b7b9c32c49242f1dfd89dcfdb6 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 03:26:50 +0000 Subject: object: containment: don't try to contain ostatus objects --- lib/pleroma/object/containment.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index f077a9f32..cd8623821 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -57,7 +57,9 @@ defmodule Pleroma.Object.Containment do id_uri = URI.parse(id) other_uri = URI.parse(other_id) - if id_uri.host == other_uri.host do + # We explicitly allow 'tag' URIs through, due to legacy OStatus objects + # being present in the ActivityPub network. + if id_uri.host == other_uri.host || other_uri.scheme == "tag" do :ok else :error -- cgit v1.2.3 From a177f22e0242f58db665688ed56dd5f1ccd1999c Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 03:41:38 +0000 Subject: object: fetcher: improve error reporting --- lib/pleroma/object/fetcher.ex | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 9436e2730..9af2e02ea 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -66,7 +66,7 @@ defmodule Pleroma.Object.Fetcher do {:normalize, nil} <- {:normalize, Object.normalize(data, false)}, params <- prepare_activity_params(data), {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)}, - {:ok, activity} <- Transmogrifier.handle_incoming(params, options), + {:transmogrifier, {:ok, activity}} <- {:transmogrifier, Transmogrifier.handle_incoming(params, options)}, {:object, _data, %Object{} = object} <- {:object, data, Object.normalize(activity, false)} do {:ok, object} @@ -74,9 +74,12 @@ defmodule Pleroma.Object.Fetcher do {:containment, _} -> {:error, "Object containment failed."} - {:error, {:reject, nil}} -> + {:transmogrifier, {:error, {:reject, nil}}} -> {:reject, nil} + {:transmogrifier, _} -> + {:error, "Transmogrifier failure."} + {:object, data, nil} -> reinject_object(%Object{}, data) @@ -106,7 +109,8 @@ defmodule Pleroma.Object.Fetcher do with {:ok, object} <- fetch_object_from_id(id, options) do object else - _e -> + e -> + Logger.error("Error while fetching #{id}: #{inspect(e)}") nil end end @@ -153,7 +157,7 @@ defmodule Pleroma.Object.Fetcher do Logger.debug("Fetch headers: #{inspect(headers)}") - with true <- String.starts_with?(id, "http"), + with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")}, {:ok, %{body: body, status: code}} when code in 200..299 <- HTTP.get(id, headers), {:ok, data} <- Jason.decode(body), :ok <- Containment.contain_origin_from_id(id, data) do @@ -162,6 +166,9 @@ defmodule Pleroma.Object.Fetcher do {:ok, %{status: code}} when code in [404, 410] -> {:error, "Object has been deleted"} + {:scheme, _} -> + {:error, "Unsupported URI scheme"} + e -> {:error, e} end -- cgit v1.2.3 From 7295a05ceee441311bf56513f5fe889908f59bd5 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 03:56:31 +0000 Subject: object: containment: also allow OStatus object IDs through when comparing origins --- lib/pleroma/object/containment.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index cd8623821..6a621ac26 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -41,7 +41,7 @@ defmodule Pleroma.Object.Containment do id_uri = URI.parse(id) actor_uri = URI.parse(get_actor(params)) - if id_uri.host == actor_uri.host do + if id_uri.host == actor_uri.host || id_uri.scheme == "tag" do :ok else :error -- cgit v1.2.3 From bae96de273250a0054d22c132bd847ab83928ca3 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 03:57:32 +0000 Subject: activitypub: tag containment checks for better error tracing --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d631e43c6..94c467b69 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -132,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, map} <- MRF.filter(map), {recipients, _, _} = get_recipients(map), {:fake, false, map, recipients} <- {:fake, fake, map, recipients}, - :ok <- Containment.contain_child(map), + {:containment, :ok} <- {:containment, Containment.contain_child(map)}, {:ok, map, object} <- insert_full_object(map) do {:ok, activity} = Repo.insert(%Activity{ -- cgit v1.2.3 From 3c785b85a62267f07f5e6bdd4f12c96ca94639c1 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 04:08:25 +0000 Subject: object: fetcher: fix up formatting --- lib/pleroma/object/fetcher.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 9af2e02ea..7758cb90b 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -66,7 +66,8 @@ defmodule Pleroma.Object.Fetcher do {:normalize, nil} <- {:normalize, Object.normalize(data, false)}, params <- prepare_activity_params(data), {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)}, - {:transmogrifier, {:ok, activity}} <- {:transmogrifier, Transmogrifier.handle_incoming(params, options)}, + {:transmogrifier, {:ok, activity}} <- + {:transmogrifier, Transmogrifier.handle_incoming(params, options)}, {:object, _data, %Object{} = object} <- {:object, data, Object.normalize(activity, false)} do {:ok, object} -- cgit v1.2.3 From e99fdfc32db231391184220eb28024d358821d27 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 15:34:36 +0000 Subject: object: containment: only allow OStatus references in test suite environment --- lib/pleroma/object/containment.ex | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 6a621ac26..1beb9c83d 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -32,6 +32,23 @@ defmodule Pleroma.Object.Containment do get_actor(%{"actor" => actor}) end + # TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus + # objects being present in the test suite environment. Once these objects are + # removed, please also remove this. + if Mix.env() == :test do + defp compare_uris(_, %URI{scheme: "tag" <> _}), do: :ok + end + + defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do + if id_uri.host == other_uri.host do + :ok + else + :error + end + end + + defp compare_uris(_, _), do: :error + @doc """ Checks that an imported AP object's actor matches the domain it came from. """ @@ -41,11 +58,7 @@ defmodule Pleroma.Object.Containment do id_uri = URI.parse(id) actor_uri = URI.parse(get_actor(params)) - if id_uri.host == actor_uri.host || id_uri.scheme == "tag" do - :ok - else - :error - end + compare_uris(actor_uri, id_uri) end def contain_origin(id, %{"attributedTo" => actor} = params), @@ -57,13 +70,7 @@ defmodule Pleroma.Object.Containment do id_uri = URI.parse(id) other_uri = URI.parse(other_id) - # We explicitly allow 'tag' URIs through, due to legacy OStatus objects - # being present in the ActivityPub network. - if id_uri.host == other_uri.host || other_uri.scheme == "tag" do - :ok - else - :error - end + compare_uris(id_uri, other_uri) end def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}), -- cgit v1.2.3 From 44e64af5e76e1ace82aa66973da883e334ebfc93 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 18 Oct 2019 15:39:15 +0000 Subject: object: containment: simplify the pattern match for OStatus testsuite hack --- lib/pleroma/object/containment.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 1beb9c83d..68535c09e 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -36,7 +36,7 @@ defmodule Pleroma.Object.Containment do # objects being present in the test suite environment. Once these objects are # removed, please also remove this. if Mix.env() == :test do - defp compare_uris(_, %URI{scheme: "tag" <> _}), do: :ok + defp compare_uris(_, %URI{scheme: "tag"}), do: :ok end defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do -- cgit v1.2.3 From 152caef51d69ba4a47ac59346c782dfff8488f8b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 18 Oct 2019 17:51:34 -0500 Subject: Direct messages should provide a distinct push notification subject --- lib/pleroma/web/push/impl.ex | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 35d3ff07c..dd445e8bf 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -125,6 +125,10 @@ defmodule Pleroma.Web.Push.Impl do end end + def format_title(%{activity: %{data: %{"directMessage" => true}}}) do + "New Direct Message" + end + def format_title(%{activity: %{data: %{"type" => type}}}) do case type do "Create" -> "New Mention" -- cgit v1.2.3 From 638457ba94931c35268cf5fb4d2a432a0def6bdd Mon Sep 17 00:00:00 2001 From: KokaKiwi Date: Fri, 11 Oct 2019 14:48:01 +0200 Subject: MastoFE: Add PWA manifest. --- lib/pleroma/web/masto_fe_controller.ex | 6 ++++++ lib/pleroma/web/router.ex | 6 ++++++ lib/pleroma/web/templates/masto_fe/index.html.eex | 6 +++++- lib/pleroma/web/views/masto_fe_view.ex | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 87860f1d5..93b38e8f4 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -34,6 +34,12 @@ defmodule Pleroma.Web.MastoFEController do end end + @doc "GET /web/manifest.json" + def manifest(conn, _params) do + conn + |> render("manifest.json") + end + @doc "PUT /api/web/settings" def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d68fb87da..e7d9ee238 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -591,6 +591,12 @@ defmodule Pleroma.Web.Router do get("/:version", Nodeinfo.NodeinfoController, :nodeinfo) end + scope "/", Pleroma.Web do + pipe_through(:api) + + get("/web/manifest.json", MastoFEController, :manifest) + end + scope "/", Pleroma.Web do pipe_through(:mastodon_html) diff --git a/lib/pleroma/web/templates/masto_fe/index.html.eex b/lib/pleroma/web/templates/masto_fe/index.html.eex index feff36fae..c330960fa 100644 --- a/lib/pleroma/web/templates/masto_fe/index.html.eex +++ b/lib/pleroma/web/templates/masto_fe/index.html.eex @@ -4,9 +4,13 @@ -<%= Pleroma.Config.get([:instance, :name]) %> +<%= Config.get([:instance, :name]) %> + + + + diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex index 21b086d4c..85b164b59 100644 --- a/lib/pleroma/web/views/masto_fe_view.ex +++ b/lib/pleroma/web/views/masto_fe_view.ex @@ -99,4 +99,23 @@ defmodule Pleroma.Web.MastoFEView do defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true + + def render("manifest.json", _params) do + %{ + name: Config.get([:instance, :name]), + description: Config.get([:instance, :description]), + icons: Config.get([:manifest, :icons]), + theme_color: Config.get([:manifest, :theme_color]), + background_color: Config.get([:manifest, :background_color]), + display: "standalone", + scope: Pleroma.Web.base_url(), + start_url: masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]), + categories: [ + "social" + ], + serviceworker: %{ + src: "/sw.js" + } + } + end end -- cgit v1.2.3 From 52ed2f8f2d962154ba61b31a3f5aab13dc7217fc Mon Sep 17 00:00:00 2001 From: eugenijm Date: Fri, 11 Oct 2019 06:40:58 +0300 Subject: Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all user's conversations as read --- lib/pleroma/conversation/participation.ex | 13 +++++++++++++ .../web/pleroma_api/controllers/pleroma_api_controller.ex | 9 +++++++++ lib/pleroma/web/router.ex | 1 + 3 files changed, 23 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index e17f49e58..41918fa78 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -69,6 +69,19 @@ defmodule Pleroma.Conversation.Participation do end end + def mark_all_as_read(user) do + {_, participations} = + __MODULE__ + |> where([p], p.user_id == ^user.id) + |> where([p], not p.read) + |> update([p], set: [read: true]) + |> select([p], p) + |> Repo.update_all([]) + + User.set_unread_conversation_count(user) + {:ok, participations} + end + def mark_as_unread(participation) do participation |> read_cng(%{read: false}) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 9d50a7ca9..fc39abf05 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -79,6 +79,15 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end end + def read_conversations(%{assigns: %{user: user}} = conn, _params) do + with {:ok, participations} <- Participation.mark_all_as_read(user) do + conn + |> add_link_headers(participations) + |> put_view(ConversationView) + |> render("participations.json", participations: participations, for: user) + end + end + def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do with {:ok, notification} <- Notification.read_one(user, notification_id) do conn diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d68fb87da..66e7a2263 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -266,6 +266,7 @@ defmodule Pleroma.Web.Router do get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) get("/conversations/:id", PleromaAPIController, :conversation) + post("/conversations/read", PleromaAPIController, :read_conversations) end scope [] do -- cgit v1.2.3 From e7333db72ade8214ad80122d49862e520b093e8a Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sat, 5 Oct 2019 20:06:03 +0000 Subject: user info: add invisible field --- lib/pleroma/user/info.ex | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 2d39abcb3..53525b386 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -53,6 +53,7 @@ defmodule Pleroma.User.Info do field(:fields, {:array, :map}, default: nil) field(:raw_fields, {:array, :map}, default: []) field(:discoverable, :boolean, default: false) + field(:invisible, :boolean, default: false) field(:notification_settings, :map, default: %{ @@ -393,6 +394,14 @@ defmodule Pleroma.User.Info do |> validate_required([:source_data]) end + def set_invisible(info, invisible) do + params = %{invisible: invisible} + + info + |> cast(params, [:invisible]) + |> validate_required([:invisible]) + end + def admin_api_update(info, params) do info |> cast(params, [ -- cgit v1.2.3 From 1469a084d4c95d24619f88c462601570f85ecfa2 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sat, 5 Oct 2019 20:06:31 +0000 Subject: relay: set invisible to true --- lib/pleroma/web/activity_pub/relay.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 03fc434a9..de80612f1 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -10,8 +10,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do require Logger def get_actor do - "#{Pleroma.Web.Endpoint.url()}/relay" - |> User.get_or_create_service_actor_by_ap_id() + actor = + "#{Pleroma.Web.Endpoint.url()}/relay" + |> User.get_or_create_service_actor_by_ap_id() + + {:ok, actor} = User.update_info(actor, &User.Info.set_invisible(&1, true)) + actor end @spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()} -- cgit v1.2.3 From 95871cb4628933900f2004a8bece1b929d5525bf Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sat, 5 Oct 2019 20:11:43 +0000 Subject: user: implement User.invisible?/1 --- lib/pleroma/user.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ec705b8f6..2bbfaa55b 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -88,6 +88,9 @@ defmodule Pleroma.User do def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true def superuser?(_), do: false + def invisible?(%User{info: %User.Info{invisible: true}}), do: true + def invisible?(_), do: false + def avatar_url(user, options \\ []) do case user.avatar do %{"url" => [%{"href" => href} | _]} -> href -- cgit v1.2.3 From ef659331b03925c41cd3e557bd39cb2d73dd6aa1 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sat, 5 Oct 2019 20:41:33 +0000 Subject: implement invisible support for remote users --- lib/pleroma/user/info.ex | 3 ++- lib/pleroma/web/activity_pub/activity_pub.ex | 10 +++++++++- lib/pleroma/web/activity_pub/transmogrifier.ex | 11 +++++++++-- lib/pleroma/web/activity_pub/utils.ex | 10 +++++++--- 4 files changed, 27 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 53525b386..982fb61c6 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -267,7 +267,8 @@ defmodule Pleroma.User.Info do :follower_count, :fields, :following_count, - :discoverable + :discoverable, + :invisible ]) |> validate_fields(true) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 94c467b69..1cfe826fa 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1107,6 +1107,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do data = Transmogrifier.maybe_fix_user_object(data) discoverable = data["discoverable"] || false + invisible = + if is_list(data["type"]) do + Enum.member?(data["type"], "Invisible") + else + false + end + user_data = %{ ap_id: data["id"], info: %{ @@ -1115,7 +1122,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do banner: banner, fields: fields, locked: locked, - discoverable: discoverable + discoverable: discoverable, + invisible: invisible }, avatar: avatar, name: data["name"], diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 2c1ce9c55..49bbeb857 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -596,13 +596,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data, _options ) - when object_type in ["Person", "Application", "Service", "Organization"] do + when object_type in [ + "Person", + "Application", + "Service", + "Organization", + ["Application", "Invisible"] + ] do with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) banner = new_user_data[:info][:banner] locked = new_user_data[:info][:locked] || false attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || [] + invisible = new_user_data[:info][:invisible] || false fields = attachment @@ -612,7 +619,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do update_data = new_user_data |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, %{banner: banner, locked: locked, fields: fields}) + |> Map.put(:info, %{banner: banner, locked: locked, fields: fields, invisible: invisible}) actor |> User.upgrade_changeset(update_data, true) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 4ef479f96..6b28df92c 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -491,10 +491,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do %Activity{data: %{"actor" => actor}}, object ) do - announcements = take_announcements(object) + unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do + announcements = take_announcements(object) - with announcements <- Enum.uniq([actor | announcements]) do - update_element_in_object("announcement", announcements, object) + with announcements <- Enum.uniq([actor | announcements]) do + update_element_in_object("announcement", announcements, object) + end + else + {:ok, object} end end -- cgit v1.2.3 From c9280b97306b894a38377acf43c62d7808df01d8 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sat, 19 Oct 2019 17:46:24 +0000 Subject: rework to use properties instead of compound typing, per SocialCG --- lib/pleroma/web/activity_pub/activity_pub.ex | 8 +------- lib/pleroma/web/activity_pub/transmogrifier.ex | 3 +-- lib/pleroma/web/activity_pub/views/user_view.ex | 3 ++- 3 files changed, 4 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1cfe826fa..9a0a3522a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1106,13 +1106,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do locked = data["manuallyApprovesFollowers"] || false data = Transmogrifier.maybe_fix_user_object(data) discoverable = data["discoverable"] || false - - invisible = - if is_list(data["type"]) do - Enum.member?(data["type"], "Invisible") - else - false - end + invisible = data["invisible"] || false user_data = %{ ap_id: data["id"], diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 49bbeb857..4a250d131 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -600,8 +600,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do "Person", "Application", "Service", - "Organization", - ["Application", "Invisible"] + "Organization" ] do with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 9b39d1629..8c5b4460b 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -55,7 +55,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do "owner" => user.ap_id, "publicKeyPem" => public_key }, - "endpoints" => endpoints + "endpoints" => endpoints, + "invisible" => User.invisible?(user) } |> Map.merge(Utils.make_json_ld_header()) end -- cgit v1.2.3 From e8843974cb9b8adfe8798bb8f7ff17b7a92f5ab8 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 20 Oct 2019 13:42:42 +0300 Subject: [#1304] Moved remaining fields from User.Info to User. Misc. fixes / improvements. --- lib/mix/tasks/pleroma/user.ex | 16 +- lib/pleroma/notification.ex | 6 +- lib/pleroma/user.ex | 228 ++++++++++++++++----- lib/pleroma/user/info.ex | 107 ---------- lib/pleroma/user/search.ex | 4 +- lib/pleroma/web/activity_pub/activity_pub.ex | 18 +- lib/pleroma/web/common_api/common_api.ex | 8 +- .../mastodon_api/controllers/account_controller.ex | 6 +- .../controllers/domain_block_controller.ex | 4 +- lib/pleroma/web/streamer/worker.ex | 8 +- .../web/twitter_api/twitter_api_controller.ex | 4 +- 11 files changed, 215 insertions(+), 194 deletions(-) delete mode 100644 lib/pleroma/user/info.ex (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 265c5f1a7..d7bdc2310 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -5,6 +5,7 @@ defmodule Mix.Tasks.Pleroma.User do use Mix.Task import Mix.Pleroma + alias Ecto.Changeset alias Pleroma.User alias Pleroma.UserInviteToken alias Pleroma.Web.OAuth @@ -364,21 +365,30 @@ defmodule Mix.Tasks.Pleroma.User do end defp set_moderator(user, value) do - {:ok, user} = User.update_and_set_cache(user, %{is_moderator: value}) + {:ok, user} = + user + |> Changeset.change(%{is_moderator: value}) + |> User.update_and_set_cache() shell_info("Moderator status of #{user.nickname}: #{user.is_moderator}") user end defp set_admin(user, value) do - {:ok, user} = User.update_and_set_cache(user, %{is_admin: value}) + {:ok, user} = + user + |> Changeset.change(%{is_admin: value}) + |> User.update_and_set_cache() shell_info("Admin status of #{user.nickname}: #{user.is_admin}") user end defp set_locked(user, value) do - {:ok, user} = User.update_and_set_cache(user, %{locked: value}) + {:ok, user} = + user + |> Changeset.change(%{locked: value}) + |> User.update_and_set_cache() shell_info("Locked status of #{user.nickname}: #{user.locked}") user diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 19d22370f..6ded91f3c 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -58,11 +58,11 @@ defmodule Pleroma.Notification do if opts[:with_muted] do query else - where(query, [n, a], a.actor not in ^user.info.muted_notifications) - |> where([n, a], a.actor not in ^user.info.blocks) + where(query, [n, a], a.actor not in ^user.muted_notifications) + |> where([n, a], a.actor not in ^user.blocks) |> where( [n, a], - fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks + fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks ) |> join(:left, [n, a], tm in Pleroma.ThreadMute, on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 165342011..d16d22198 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -122,7 +122,8 @@ defmodule Pleroma.User do has_many(:notifications, Notification) has_many(:registrations, Registration) has_many(:deliveries, Delivery) - embeds_one(:info, User.Info) + + field(:info, :map, default: %{}) timestamps() end @@ -304,14 +305,39 @@ defmodule Pleroma.User do changeset = %User{local: false} - |> cast(params, [:bio, :name, :ap_id, :nickname, :avatar] ++ @info_fields) + |> cast( + params, + [ + :bio, + :name, + :ap_id, + :nickname, + :avatar, + :ap_enabled, + :source_data, + :banner, + :locked, + :magic_key, + :uri, + :hub, + :topic, + :salmon, + :hide_followers, + :hide_follows, + :hide_followers_count, + :hide_follows_count, + :follower_count, + :fields, + :following_count, + :discoverable + ] + ) |> validate_required([:name, :ap_id]) |> unique_constraint(:nickname) |> validate_format(:nickname, @email_regex) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, max: name_limit) |> validate_fields(true) - |> change_info(& &1) case params[:source_data] do %{"followers" => followers, "following" => following} -> @@ -330,7 +356,31 @@ defmodule Pleroma.User do name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) struct - |> cast(params, [:bio, :name, :avatar, :following] ++ @info_fields) + |> cast( + params, + [ + :bio, + :name, + :avatar, + :following, + :locked, + :no_rich_text, + :default_scope, + :banner, + :hide_follows, + :hide_followers, + :hide_followers_count, + :hide_follows_count, + :hide_favorites, + :background, + :show_role, + :skip_thread_containment, + :fields, + :raw_fields, + :pleroma_settings_store, + :discoverable + ] + ) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) @@ -355,15 +405,27 @@ defmodule Pleroma.User do :follower_address, :following_address, :avatar, - :last_refreshed_at - ] ++ @info_fields + :last_refreshed_at, + :ap_enabled, + :source_data, + :banner, + :locked, + :magic_key, + :follower_count, + :following_count, + :hide_follows, + :fields, + :hide_followers, + :discoverable, + :hide_followers_count, + :hide_follows_count + ] ) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, max: name_limit) |> validate_fields(remote?) - |> change_info(& &1) end def password_update_changeset(struct, params) do @@ -426,7 +488,6 @@ defmodule Pleroma.User do |> validate_format(:email, @email_regex) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, min: 1, max: name_limit) - |> change_info(& &1) |> maybe_validate_required_email(opts[:external]) |> put_password_hash |> put_ap_id() @@ -884,7 +945,9 @@ defmodule Pleroma.User do ) |> Repo.one() - update_and_set_cache(user, %{note_count: note_count}) + user + |> cast(%{note_count: note_count}, [:note_count]) + |> update_and_set_cache() end @spec maybe_fetch_follow_information(User.t()) :: User.t() @@ -1023,11 +1086,11 @@ defmodule Pleroma.User do @spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()} def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do - update_info(muter, &User.Info.add_to_mutes(&1, ap_id, notifications?)) + add_to_mutes(muter, ap_id, notifications?) end def unmute(muter, %{ap_id: ap_id}) do - update_info(muter, &User.Info.remove_from_mutes(&1, ap_id)) + remove_from_mutes(muter, ap_id) end def subscribe(subscriber, %{ap_id: ap_id}) do @@ -1077,7 +1140,7 @@ defmodule Pleroma.User do {:ok, blocker} = update_follower_count(blocker) - update_info(blocker, &User.Info.add_to_block(&1, ap_id)) + add_to_block(blocker, ap_id) end # helper to handle the block given only an actor's AP id @@ -1086,17 +1149,17 @@ defmodule Pleroma.User do end def unblock(blocker, %{ap_id: ap_id}) do - update_info(blocker, &User.Info.remove_from_block(&1, ap_id)) + remove_from_block(blocker, ap_id) end def mutes?(nil, _), do: false - def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id) + def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.mutes, ap_id) @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean() def muted_notifications?(nil, _), do: false def muted_notifications?(user, %{ap_id: ap_id}), - do: Enum.member?(user.info.muted_notifications, ap_id) + do: Enum.member?(user.muted_notifications, ap_id) def blocks?(%User{} = user, %User{} = target) do blocks_ap_id?(user, target) || blocks_domain?(user, target) @@ -1105,13 +1168,13 @@ defmodule Pleroma.User do def blocks?(nil, _), do: false def blocks_ap_id?(%User{} = user, %User{} = target) do - Enum.member?(user.info.blocks, target.ap_id) + Enum.member?(user.blocks, target.ap_id) end def blocks_ap_id?(_, _), do: false def blocks_domain?(%User{} = user, %User{} = target) do - domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks) + domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks) %{host: host} = URI.parse(target.ap_id) Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host) end @@ -1126,13 +1189,13 @@ defmodule Pleroma.User do @spec muted_users(User.t()) :: [User.t()] def muted_users(user) do - User.Query.build(%{ap_id: user.info.mutes, deactivated: false}) + User.Query.build(%{ap_id: user.mutes, deactivated: false}) |> Repo.all() end @spec blocked_users(User.t()) :: [User.t()] def blocked_users(user) do - User.Query.build(%{ap_id: user.info.blocks, deactivated: false}) + User.Query.build(%{ap_id: user.blocks, deactivated: false}) |> Repo.all() end @@ -1142,14 +1205,6 @@ defmodule Pleroma.User do |> Repo.all() end - def block_domain(user, domain) do - update_info(user, &User.Info.add_to_domain_block(&1, domain)) - end - - def unblock_domain(user, domain) do - update_info(user, &User.Info.remove_from_domain_block(&1, domain)) - end - def deactivate_async(user, status \\ true) do BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status}) end @@ -1385,7 +1440,7 @@ defmodule Pleroma.User do else _ -> {:ok, user} = - %User{info: %User.Info{}} + %User{} |> cast(%{}, [:ap_id, :nickname, :local]) |> put_change(:ap_id, uri) |> put_change(:nickname, nickname) @@ -1549,7 +1604,6 @@ defmodule Pleroma.User do %User{ name: ap_id, ap_id: ap_id, - info: %User.Info{}, nickname: "erroruser@example.com", inserted_at: NaiveDateTime.utc_now() } @@ -1562,7 +1616,7 @@ defmodule Pleroma.User do end def showing_reblogs?(%User{} = user, %User{} = target) do - target.ap_id not in user.info.muted_reblogs + target.ap_id not in user.muted_reblogs end @doc """ @@ -1741,28 +1795,6 @@ defmodule Pleroma.User do |> update_and_set_cache() end - @doc """ - Changes `user.info` and returns the user changeset. - - `fun` is called with the `user.info`. - """ - def change_info(user, fun) do - changeset = change(user) - info = get_field(changeset, :info) || %User.Info{} - put_embed(changeset, :info, fun.(info)) - end - - @doc """ - Updates `user.info` and sets cache. - - `fun` is called with the `user.info`. - """ - def update_info(user, fun) do - user - |> change_info(fun) - |> update_and_set_cache() - end - def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do %{ admin: is_admin, @@ -1918,4 +1950,96 @@ defmodule Pleroma.User do def remove_from_subscribers(user, subscribed) do set_subscribers(user, List.delete(user.subscribers, subscribed)) end + + defp set_domain_blocks(user, domain_blocks) do + params = %{domain_blocks: domain_blocks} + + user + |> cast(params, [:domain_blocks]) + |> validate_required([:domain_blocks]) + |> update_and_set_cache() + end + + def block_domain(user, domain_blocked) do + set_domain_blocks(user, Enum.uniq([domain_blocked | user.domain_blocks])) + end + + def unblock_domain(user, domain_blocked) do + set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked)) + end + + defp set_blocks(user, blocks) do + params = %{blocks: blocks} + + user + |> cast(params, [:blocks]) + |> validate_required([:blocks]) + |> update_and_set_cache() + end + + def add_to_block(user, blocked) do + set_blocks(user, Enum.uniq([blocked | user.blocks])) + end + + def remove_from_block(user, blocked) do + set_blocks(user, List.delete(user.blocks, blocked)) + end + + defp set_mutes(user, mutes) do + params = %{mutes: mutes} + + user + |> cast(params, [:mutes]) + |> validate_required([:mutes]) + |> update_and_set_cache() + end + + def add_to_mutes(user, muted, notifications?) do + with {:ok, user} <- set_mutes(user, Enum.uniq([muted | user.mutes])) do + set_notification_mutes( + user, + Enum.uniq([muted | user.muted_notifications]), + notifications? + ) + end + end + + def remove_from_mutes(user, muted) do + with {:ok, user} <- set_mutes(user, List.delete(user.mutes, muted)) do + set_notification_mutes( + user, + List.delete(user.muted_notifications, muted), + true + ) + end + end + + defp set_notification_mutes(user, _muted_notifications, _notifications? = false) do + {:ok, user} + end + + defp set_notification_mutes(user, muted_notifications, _notifications? = true) do + params = %{muted_notifications: muted_notifications} + + user + |> cast(params, [:muted_notifications]) + |> validate_required([:muted_notifications]) + |> update_and_set_cache() + end + + def add_reblog_mute(user, ap_id) do + params = %{muted_reblogs: user.muted_reblogs ++ [ap_id]} + + user + |> cast(params, [:muted_reblogs]) + |> update_and_set_cache() + end + + def remove_reblog_mute(user, ap_id) do + params = %{muted_reblogs: List.delete(user.muted_reblogs, ap_id)} + + user + |> cast(params, [:muted_reblogs]) + |> update_and_set_cache() + end end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex deleted file mode 100644 index fe59ddeab..000000000 --- a/lib/pleroma/user/info.ex +++ /dev/null @@ -1,107 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.Info do - use Ecto.Schema - import Ecto.Changeset - - alias Pleroma.User.Info - - @type t :: %__MODULE__{} - - embedded_schema do - field(:blocks, {:array, :string}, default: []) - field(:domain_blocks, {:array, :string}, default: []) - field(:mutes, {:array, :string}, default: []) - field(:muted_reblogs, {:array, :string}, default: []) - field(:muted_notifications, {:array, :string}, default: []) - - # Found in the wild - # ap_id -> Where is this used? - # bio -> Where is this used? - # avatar -> Where is this used? - # fqn -> Where is this used? - # host -> Where is this used? - # subject _> Where is this used? - end - - def set_mutes(info, mutes) do - params = %{mutes: mutes} - - info - |> cast(params, [:mutes]) - |> validate_required([:mutes]) - end - - @spec set_notification_mutes(Changeset.t(), [String.t()], boolean()) :: Changeset.t() - def set_notification_mutes(changeset, muted_notifications, notifications?) do - if notifications? do - put_change(changeset, :muted_notifications, muted_notifications) - |> validate_required([:muted_notifications]) - else - changeset - end - end - - def set_blocks(info, blocks) do - params = %{blocks: blocks} - - info - |> cast(params, [:blocks]) - |> validate_required([:blocks]) - end - - @spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t() - def add_to_mutes(info, muted, notifications?) do - info - |> set_mutes(Enum.uniq([muted | info.mutes])) - |> set_notification_mutes( - Enum.uniq([muted | info.muted_notifications]), - notifications? - ) - end - - @spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t() - def remove_from_mutes(info, muted) do - info - |> set_mutes(List.delete(info.mutes, muted)) - |> set_notification_mutes(List.delete(info.muted_notifications, muted), true) - end - - def add_to_block(info, blocked) do - set_blocks(info, Enum.uniq([blocked | info.blocks])) - end - - def remove_from_block(info, blocked) do - set_blocks(info, List.delete(info.blocks, blocked)) - end - - def set_domain_blocks(info, domain_blocks) do - params = %{domain_blocks: domain_blocks} - - info - |> cast(params, [:domain_blocks]) - |> validate_required([:domain_blocks]) - end - - def add_to_domain_block(info, domain_blocked) do - set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks])) - end - - def remove_from_domain_block(info, domain_blocked) do - set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked)) - end - - def add_reblog_mute(info, ap_id) do - params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]} - - cast(info, params, [:muted_reblogs]) - end - - def remove_reblog_mute(info, ap_id) do - params = %{muted_reblogs: List.delete(info.muted_reblogs, ap_id)} - - cast(info, params, [:muted_reblogs]) - end -end diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index 6fb2c2352..7fffb1e55 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -68,14 +68,14 @@ defmodule Pleroma.User.Search do defp base_query(_user, false), do: User defp base_query(user, true), do: User.get_followers_query(user) - defp filter_blocked_user(query, %User{info: %{blocks: blocks}}) + defp filter_blocked_user(query, %User{blocks: blocks}) when length(blocks) > 0 do from(q in query, where: not (q.ap_id in ^blocks)) end defp filter_blocked_user(query, _), do: query - defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}}) + defp filter_blocked_domains(query, %User{domain_blocks: domain_blocks}) when length(domain_blocks) > 0 do domains = Enum.join(domain_blocks, ",") diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 78efaae2a..b9e88ec70 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -235,8 +235,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), _ <- increase_poll_votes_if_vote(create_data), - # Changing note count prior to enqueuing federation task in order to avoid - # race conditions on updating user.info {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} @@ -429,8 +427,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, activity} <- insert(data, local, false), stream_out_participations(object, user), _ <- decrease_replies_count_if_reply(object), - # Changing note count prior to enqueuing federation task in order to avoid - # race conditions on updating user.info {:ok, _actor} <- decrease_note_count_if_public(user, object), :ok <- maybe_federate(activity) do {:ok, activity} @@ -800,8 +796,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query - defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do - mutes = info.mutes + defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do + mutes = user.mutes query = from([activity] in query, @@ -818,9 +814,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted(query, _), do: query - defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do - blocks = info.blocks || [] - domain_blocks = info.domain_blocks || [] + defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do + blocks = user.blocks || [] + domain_blocks = user.domain_blocks || [] query = if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query) @@ -861,8 +857,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_pinned(query, _), do: query - defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do - muted_reblogs = info.muted_reblogs || [] + defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do + muted_reblogs = user.muted_reblogs || [] from( activity in query, diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index ef738a870..449b808b5 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -392,14 +392,14 @@ defmodule Pleroma.Web.CommonAPI do defp set_visibility(activity, _), do: {:ok, activity} def hide_reblogs(user, %{ap_id: ap_id} = _muted) do - if ap_id not in user.info.muted_reblogs do - User.update_info(user, &User.Info.add_reblog_mute(&1, ap_id)) + if ap_id not in user.muted_reblogs do + User.add_reblog_mute(user, ap_id) end end def show_reblogs(user, %{ap_id: ap_id} = _muted) do - if ap_id in user.info.muted_reblogs do - User.update_info(user, &User.Info.remove_reblog_mute(&1, ap_id)) + if ap_id in user.muted_reblogs do + User.remove_reblog_mute(user, ap_id) end end end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 7ba2f9ff1..73fad519e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -197,11 +197,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> Enum.dedup() user_params = Map.put(user_params, :emoji, user_emojis) - - changeset = - user - |> User.update_changeset(user_params) - |> User.change_info(& &1) + changeset = User.update_changeset(user, user_params) with {:ok, user} <- User.update_and_set_cache(changeset) do if original_user != user, do: CommonAPI.update(user) diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index c7606246b..456fe7ab2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -21,8 +21,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) @doc "GET /api/v1/domain_blocks" - def index(%{assigns: %{user: %{info: info}}} = conn, _) do - json(conn, Map.get(info, :domain_blocks, [])) + def index(%{assigns: %{user: user}} = conn, _) do + json(conn, Map.get(user, :domain_blocks, [])) end @doc "POST /api/v1/domain_blocks" diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index 9910c3484..c2ee9e1f5 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -129,12 +129,12 @@ defmodule Pleroma.Web.Streamer.Worker do end defp should_send?(%User{} = user, %Activity{} = item) do - blocks = user.info.blocks || [] - mutes = user.info.mutes || [] - reblog_mutes = user.info.muted_reblogs || [] + blocks = user.blocks || [] + mutes = user.mutes || [] + reblog_mutes = user.muted_reblogs || [] recipient_blocks = MapSet.new(blocks ++ mutes) recipients = MapSet.new(item.recipients) - domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks) + domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks) with parent when not is_nil(parent) <- Object.normalize(item), true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)), diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 5eafc01de..39f10c49f 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -23,7 +23,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do with %User{} = user <- User.get_cached_by_id(uid), true <- user.local and user.confirmation_pending and user.confirmation_token == token, {:ok, _} <- - User.update_and_set_cache(User.confirmation_changeset(user, need_confirmation: false)) do + user + |> User.confirmation_changeset(need_confirmation: false) + |> User.update_and_set_cache() do redirect(conn, to: "/") end end -- cgit v1.2.3 From b4cbf0568bd6924ded9b9fbdb588a4da92bf83e7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 20 Oct 2019 13:58:27 +0300 Subject: [#1304] Analysis issue fix. --- lib/pleroma/user.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d16d22198..282cf6962 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2014,11 +2014,11 @@ defmodule Pleroma.User do end end - defp set_notification_mutes(user, _muted_notifications, _notifications? = false) do + defp set_notification_mutes(user, _muted_notifications, false = _notifications?) do {:ok, user} end - defp set_notification_mutes(user, muted_notifications, _notifications? = true) do + defp set_notification_mutes(user, muted_notifications, true = _notifications?) do params = %{muted_notifications: muted_notifications} user -- cgit v1.2.3 From ee04fbc35ad56784614c8cdd3f863da284a5725e Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 20 Oct 2019 22:29:56 +0300 Subject: [#1304]. Post-merge fixes. Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into 1304-user-info-deprecation # Conflicts: # CHANGELOG.md # lib/pleroma/notification.ex # lib/pleroma/user.ex # lib/pleroma/user/info.ex # lib/pleroma/web/admin_api/admin_api_controller.ex # lib/pleroma/web/ostatus/handlers/follow_handler.ex # lib/pleroma/web/ostatus/ostatus.ex # lib/pleroma/web/salmon/salmon.ex # lib/pleroma/web/websub/websub.ex # test/web/admin_api/admin_api_controller_test.exs # test/web/federator_test.exs # test/web/mastodon_api/controllers/conversation_controller_test.exs # test/web/ostatus/ostatus_controller_test.exs # test/web/ostatus/ostatus_test.exs # test/web/salmon/salmon_test.exs # test/web/websub/websub_test.exs --- lib/pleroma/user.ex | 11 +------ lib/pleroma/web/admin_api/admin_api_controller.ex | 36 ++++++----------------- 2 files changed, 10 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e2ebb7d64..89f88c74d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -89,9 +89,6 @@ defmodule Pleroma.User do field(:settings, :map, default: nil) field(:magic_key, :string, default: nil) field(:uri, :string, default: nil) - field(:topic, :string, default: nil) - field(:hub, :string, default: nil) - field(:salmon, :string, default: nil) field(:hide_followers_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false) field(:hide_followers, :boolean, default: false) @@ -250,9 +247,6 @@ defmodule Pleroma.User do :settings, :magic_key, :uri, - :topic, - :hub, - :salmon, :hide_followers_count, :hide_follows_count, :hide_followers, @@ -317,9 +311,6 @@ defmodule Pleroma.User do :locked, :magic_key, :uri, - :hub, - :topic, - :salmon, :hide_followers, :hide_follows, :hide_followers_count, @@ -1453,7 +1444,7 @@ defmodule Pleroma.User do {:ok, key} end - def public_key_from_info(_), do: {:error, "not found key"} + def public_key(_), do: {:error, "not found key"} def get_public_key_for_ap_id(ap_id) do with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 66b5276d4..7ffbb23e7 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -51,8 +51,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do :tag_users, :untag_users, :right_add, - :right_delete, - :set_activation_status + :right_delete ] ) @@ -364,11 +363,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do "nicknames" => nicknames }) when permission_group in ["moderator", "admin"] do - info = Map.put(%{}, "is_" <> permission_group, true) + update = %{:"is_#{permission_group}" => true} users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) - User.update_info(users, &User.Info.admin_api_update(&1, info)) + for u <- users, do: User.admin_api_update(u, update) ModerationLog.insert_log(%{ action: "grant", @@ -377,7 +376,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do permission: permission_group }) - json(conn, info) + json(conn, update) end def right_add_multiple(conn, _) do @@ -429,11 +428,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ) when permission_group in ["moderator", "admin"] do with false <- Enum.member?(nicknames, admin_nickname) do - info = Map.put(%{}, "is_" <> permission_group, false) + update = %{:"is_#{permission_group}" => false} users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) - User.update_info(users, &User.Info.admin_api_update(&1, info)) + for u <- users, do: User.admin_api_update(u, update) ModerationLog.insert_log(%{ action: "revoke", @@ -442,7 +441,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do permission: permission_group }) - json(conn, info) + json(conn, update) else _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.") end @@ -481,25 +480,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do render_error(conn, :forbidden, "You can't revoke your own admin status.") end - def set_activation_status(%{assigns: %{user: admin}} = conn, %{ - "nickname" => nickname, - "status" => status - }) do - with {:ok, status} <- Ecto.Type.cast(:boolean, status), - %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, _} <- User.deactivate(user, !status) do - action = if(user.deactivated, do: "activate", else: "deactivate") - - ModerationLog.insert_log(%{ - actor: admin, - subject: user, - action: action - }) - - json_response(conn, :no_content, "") - end - end - def relay_list(conn, _params) do with {:ok, list} <- Relay.list() do json(conn, %{relays: list}) @@ -507,6 +487,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do _ -> conn |> put_status(500) + end + end def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do with {:ok, _message} <- Relay.follow(target) do -- cgit v1.2.3 From 7c7f90bc4f361b8bf4b49790640a4aa490ee619f Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 21 Oct 2019 11:58:22 +0300 Subject: [#1304] Merged `develop`, handled User.Info.invisible. --- lib/pleroma/user.ex | 16 ++++++++++++++-- lib/pleroma/web/activity_pub/relay.ex | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 00a1df133..e8038c1c1 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -103,6 +103,7 @@ defmodule Pleroma.User do field(:fields, {:array, :map}, default: nil) field(:raw_fields, {:array, :map}, default: []) field(:discoverable, :boolean, default: false) + field(:invisible, :boolean, default: false) field(:skip_thread_containment, :boolean, default: false) field(:notification_settings, :map, @@ -142,7 +143,7 @@ defmodule Pleroma.User do def superuser?(%User{local: true, is_moderator: true}), do: true def superuser?(_), do: false - def invisible?(%User{info: %User.Info{invisible: true}}), do: true + def invisible?(%User{invisible: true}), do: true def invisible?(_), do: false def avatar_url(user, options \\ []) do @@ -264,6 +265,7 @@ defmodule Pleroma.User do :fields, :raw_fields, :discoverable, + :invisible, :skip_thread_containment, :notification_settings ] @@ -321,7 +323,8 @@ defmodule Pleroma.User do :follower_count, :fields, :following_count, - :discoverable + :discoverable, + :invisible ] ) |> validate_required([:name, :ap_id]) @@ -2021,4 +2024,13 @@ defmodule Pleroma.User do |> cast(params, [:muted_reblogs]) |> update_and_set_cache() end + + def set_invisible(user, invisible) do + params = %{invisible: invisible} + + user + |> cast(params, [:invisible]) + |> validate_required([:invisible]) + |> update_and_set_cache() + end end diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index de80612f1..a9434d75c 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do "#{Pleroma.Web.Endpoint.url()}/relay" |> User.get_or_create_service_actor_by_ap_id() - {:ok, actor} = User.update_info(actor, &User.Info.set_invisible(&1, true)) + {:ok, actor} = User.set_invisible(actor, true) actor end -- cgit v1.2.3 From 2dbee29cf5a1a17438e8bc3d2586717da64e20ff Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 21 Oct 2019 12:38:35 +0300 Subject: Do not include notifications from blocked users when with_muted is set This is not what with_muted is for per documentation and it was agreed on irc that this behavior doesn't make sense. --- lib/pleroma/notification.ex | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index d145f8d5b..e5da1492b 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -55,9 +55,19 @@ defmodule Pleroma.Notification do ) |> preload([n, a, o], activity: {a, object: o}) |> exclude_muted(user, opts) + |> exclude_blocked(user) |> exclude_visibility(opts) end + defp exclude_blocked(query, user) do + query + |> where([n, a], a.actor not in ^user.info.blocks) + |> where( + [n, a], + fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks + ) + end + defp exclude_muted(query, _, %{with_muted: true}) do query end @@ -65,11 +75,6 @@ defmodule Pleroma.Notification do defp exclude_muted(query, user, _opts) do query |> where([n, a], a.actor not in ^user.info.muted_notifications) - |> where([n, a], a.actor not in ^user.info.blocks) - |> where( - [n, a], - fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks - ) |> join(:left, [n, a], tm in Pleroma.ThreadMute, on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data) ) -- cgit v1.2.3 From 3f6a596922fa6839ca26b3136074e4cf054458ad Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Tue, 22 Oct 2019 20:50:25 -0500 Subject: activitypub: visibility: refactor is_public?() to use JSON-LD safe accessors --- lib/pleroma/web/activity_pub/utils.ex | 42 ++++++++++++++++-------------- lib/pleroma/web/activity_pub/visibility.ex | 3 ++- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 6b28df92c..699d5162f 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -49,26 +49,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do def determine_explicit_mentions(_), do: [] - @spec recipient_in_collection(any(), any()) :: boolean() - defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll - defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll - defp recipient_in_collection(_, _), do: false + @spec label_in_collection?(any(), any()) :: boolean() + defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll + defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll + defp label_in_collection?(_, _), do: false + + @spec label_in_message?(String.t(), map()) :: boolean() + def label_in_message?(label, params), + do: + [params["to"], params["cc"], params["bto"], params["bcc"]] + |> Enum.any?(&label_in_collection?(label, &1)) + + @spec unaddressed_message?(map()) :: boolean() + def unaddressed_message?(params), + do: + [params["to"], params["cc"], params["bto"], params["bcc"]] + |> Enum.all?(&is_nil(&1)) @spec recipient_in_message(User.t(), User.t(), map()) :: boolean() - def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do - addresses = [params["to"], params["cc"], params["bto"], params["bcc"]] - - cond do - Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true - # if the message is unaddressed at all, then assume it is directly addressed - # to the recipient - Enum.all?(addresses, &is_nil(&1)) -> true - # if the message is sent from somebody the user is following, then assume it - # is addressed to the recipient - User.following?(recipient, actor) -> true - true -> false - end - end + def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params), + do: + label_in_message?(ap_id, params) || unaddressed_message?(params) || + User.following?(recipient, actor) defp extract_list(target) when is_binary(target), do: [target] defp extract_list(lst) when is_list(lst), do: lst @@ -76,8 +78,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do def maybe_splice_recipient(ap_id, params) do need_splice? = - !recipient_in_collection(ap_id, params["to"]) && - !recipient_in_collection(ap_id, params["cc"]) + !label_in_collection?(ap_id, params["to"]) && + !label_in_collection?(ap_id, params["cc"]) if need_splice? do cc_list = extract_list(params["cc"]) diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 270d0fa02..f3ab48f7c 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.ActivityPub.Utils require Pleroma.Constants @@ -15,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do def is_public?(%Object{data: data}), do: is_public?(data) def is_public?(%Activity{data: data}), do: is_public?(data) def is_public?(%{"directMessage" => true}), do: false - def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || [])) + def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data) def is_private?(activity) do with false <- is_public?(activity), -- cgit v1.2.3 From 11cd9944258bfc2123b821b21321e12097d0f19b Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 23 Oct 2019 17:15:48 +0300 Subject: [#1304] Fixed null::jsonb handling in User.Info migration. --- lib/pleroma/user.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e8038c1c1..7bef6e281 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -100,7 +100,7 @@ defmodule Pleroma.User do field(:mascot, :map, default: nil) field(:emoji, {:array, :map}, default: []) field(:pleroma_settings_store, :map, default: %{}) - field(:fields, {:array, :map}, default: nil) + field(:fields, {:array, :map}, default: []) field(:raw_fields, {:array, :map}, default: []) field(:discoverable, :boolean, default: false) field(:invisible, :boolean, default: false) -- cgit v1.2.3 From 065e9fac12c1ea06eca2ff55a8253d4bc23362e1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 23 Oct 2019 17:31:02 +0300 Subject: [#1304] Adjusted User.Info references from Notification. --- lib/pleroma/notification.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 83844d3be..b7ecf51e4 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -61,10 +61,10 @@ defmodule Pleroma.Notification do defp exclude_blocked(query, user) do query - |> where([n, a], a.actor not in ^user.info.blocks) + |> where([n, a], a.actor not in ^user.blocks) |> where( [n, a], - fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks + fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks ) end -- cgit v1.2.3 From c0246309a5013b17a405644a0918ec0d3569eaf1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 23 Oct 2019 19:30:02 +0300 Subject: [#1304] Extra bullet-proofing. --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 7fc5051ea..07dde3537 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1164,7 +1164,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with {:enabled, true} <- {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])}, {:ok, info} <- fetch_follow_information_for_user(data) do - info = Map.merge(data.info, info) + info = Map.merge(data[:info] || %{}, info) Map.put(data, :info, info) else {:enabled, false} -> -- cgit v1.2.3 From 2ab072f94947ee7ee719eff86f012e6302eeeacd Mon Sep 17 00:00:00 2001 From: stwf Date: Thu, 24 Oct 2019 12:08:34 -0400 Subject: object fetcher error handling --- lib/pleroma/object/fetcher.ex | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 7758cb90b..441ae8b65 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -90,6 +90,9 @@ defmodule Pleroma.Object.Fetcher do {:fetch_object, %Object{} = object} -> {:ok, object} + {:fetch, {:error, error}} -> + {:error, error} + e -> e end @@ -110,6 +113,9 @@ defmodule Pleroma.Object.Fetcher do with {:ok, object} <- fetch_object_from_id(id, options) do object else + {:error, %Tesla.Mock.Error{}} -> + nil + e -> Logger.error("Error while fetching #{id}: #{inspect(e)}") nil @@ -170,6 +176,9 @@ defmodule Pleroma.Object.Fetcher do {:scheme, _} -> {:error, "Unsupported URI scheme"} + {:error, e} -> + {:error, e} + e -> {:error, e} end -- cgit v1.2.3 From dbee53c2df797dae4bf72d99339af12c6c522ba9 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 25 Oct 2019 16:48:01 +0700 Subject: Remove unused Pleroma.User.info_fields/0 --- lib/pleroma/user.ex | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7bef6e281..b99e236a4 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -224,54 +224,6 @@ defmodule Pleroma.User do |> Repo.aggregate(:count, :id) end - @info_fields [ - :banner, - :background, - :source_data, - :note_count, - :follower_count, - :following_count, - :locked, - :confirmation_pending, - :password_reset_pending, - :confirmation_token, - :default_scope, - :blocks, - :domain_blocks, - :mutes, - :muted_reblogs, - :muted_notifications, - :subscribers, - :deactivated, - :no_rich_text, - :ap_enabled, - :is_moderator, - :is_admin, - :show_role, - :settings, - :magic_key, - :uri, - :hide_followers_count, - :hide_follows_count, - :hide_followers, - :hide_follows, - :hide_favorites, - :unread_conversation_count, - :pinned_activities, - :email_notifications, - :mascot, - :emoji, - :pleroma_settings_store, - :fields, - :raw_fields, - :discoverable, - :invisible, - :skip_thread_containment, - :notification_settings - ] - - def info_fields, do: @info_fields - defp truncate_fields_param(params) do if Map.has_key?(params, :fields) do Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1)) -- cgit v1.2.3 From 058065448510af59f550ec7d7f99b81388bc7a40 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Fri, 25 Oct 2019 21:29:23 +0300 Subject: Mark the conversations with the blocked user as read and update the blocking user's `unread_conversation_count`. Since the conversations with the blocked user are invisible, they are excluded from the blocking user's `unread_conversation_count`. --- lib/pleroma/conversation.ex | 8 ++++- lib/pleroma/conversation/participation.ex | 38 +++++++++++++++++++--- lib/pleroma/user.ex | 6 ++-- .../controllers/pleroma_api_controller.ex | 2 +- 4 files changed, 44 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 098016af2..ade3a526a 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -67,7 +67,13 @@ defmodule Pleroma.Conversation do participations = Enum.map(users, fn user -> - User.increment_unread_conversation_count(conversation, user) + invisible_conversation = Enum.any?(users, &User.blocks?(user, &1)) + + unless invisible_conversation do + User.increment_unread_conversation_count(conversation, user) + end + + opts = Keyword.put(opts, :invisible_conversation, invisible_conversation) {:ok, participation} = Participation.create_for_user_and_conversation(user, conversation, opts) diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 41918fa78..176b82a20 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -32,11 +32,20 @@ defmodule Pleroma.Conversation.Participation do def create_for_user_and_conversation(user, conversation, opts \\ []) do read = !!opts[:read] + invisible_conversation = !!opts[:invisible_conversation] + + update_on_conflict = + if(invisible_conversation, do: [], else: [read: read]) + |> Keyword.put(:updated_at, NaiveDateTime.utc_now()) %__MODULE__{} - |> creation_cng(%{user_id: user.id, conversation_id: conversation.id, read: read}) + |> creation_cng(%{ + user_id: user.id, + conversation_id: conversation.id, + read: invisible_conversation || read + }) |> Repo.insert( - on_conflict: [set: [read: read, updated_at: NaiveDateTime.utc_now()]], + on_conflict: [set: update_on_conflict], returning: true, conflict_target: [:user_id, :conversation_id] ) @@ -69,7 +78,26 @@ defmodule Pleroma.Conversation.Participation do end end - def mark_all_as_read(user) do + def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do + target_conversation_ids = + __MODULE__ + |> where([p], p.user_id == ^target_user.id) + |> select([p], p.conversation_id) + |> Repo.all() + + __MODULE__ + |> where([p], p.user_id == ^user.id) + |> where([p], p.conversation_id in ^target_conversation_ids) + |> update([p], set: [read: true]) + |> Repo.update_all([]) + + {:ok, user} = User.set_unread_conversation_count(user) + {:ok, user, []} + end + + def mark_all_as_read(%User{} = user, %User{}), do: {:ok, user, []} + + def mark_all_as_read(%User{} = user) do {_, participations} = __MODULE__ |> where([p], p.user_id == ^user.id) @@ -78,8 +106,8 @@ defmodule Pleroma.Conversation.Participation do |> select([p], p) |> Repo.update_all([]) - User.set_unread_conversation_count(user) - {:ok, participations} + {:ok, user} = User.set_unread_conversation_count(user) + {:ok, user, participations} end def mark_as_unread(participation) do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index b99e236a4..5d3f55721 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -971,7 +971,7 @@ defmodule Pleroma.User do end end - def set_unread_conversation_count(_), do: :noop + def set_unread_conversation_count(user), do: {:ok, user} def increment_unread_conversation_count(conversation, %User{local: true} = user) do unread_query = @@ -993,7 +993,7 @@ defmodule Pleroma.User do end end - def increment_unread_conversation_count(_, _), do: :noop + def increment_unread_conversation_count(_, user), do: {:ok, user} def remove_duplicated_following(%User{following: following} = user) do uniq_following = Enum.uniq(following) @@ -1077,7 +1077,7 @@ defmodule Pleroma.User do if following?(blocked, blocker), do: unfollow(blocked, blocker) {:ok, blocker} = update_follower_count(blocker) - + {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked) add_to_block(blocker, ap_id) end diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index fc39abf05..651a99423 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -80,7 +80,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end def read_conversations(%{assigns: %{user: user}} = conn, _params) do - with {:ok, participations} <- Participation.mark_all_as_read(user) do + with {:ok, _, participations} <- Participation.mark_all_as_read(user) do conn |> add_link_headers(participations) |> put_view(ConversationView) -- cgit v1.2.3