Merge remote-tracking branch 'remotes/upstream/develop' into 161-incoming-replies-depth-limit

This commit is contained in:
Ivan Tashkinov 2019-07-06 10:17:06 +03:00
commit ad8d86e7c6
87 changed files with 510 additions and 330 deletions

View file

@ -35,6 +35,7 @@ docs-build:
- develop@pleroma/pleroma - develop@pleroma/pleroma
variables: variables:
MIX_ENV: dev MIX_ENV: dev
PLEROMA_BUILD_ENV: prod
script: script:
- mix deps.get - mix deps.get
- mix compile - mix compile

View file

@ -8,13 +8,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`) - MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
- Federation: Support for restricting max. reply-to depth on fetching - Federation: Support for restricting max. reply-to depth on fetching
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses) - Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
### Fixed ### Fixed
- Not being able to pin unlisted posts - Not being able to pin unlisted posts
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
### Changed ### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
### Changed
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
## [1.0.0] - 2019-06-29 ## [1.0.0] - 2019-06-29
### Security ### Security
- Mastodon API: Fix display names not being sanitized - Mastodon API: Fix display names not being sanitized

View file

@ -38,7 +38,9 @@ Authentication is required and the user must be an admin.
"moderator": bool "moderator": bool
}, },
"local": bool, "local": bool,
"tags": array "tags": array,
"avatar": string,
"display_name": string
}, },
... ...
] ]
@ -331,6 +333,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
"pleroma": {}, "pleroma": {},
"sensitive": false "sensitive": false
}, },
"tags": ["force_unlisted"],
"statuses_count": 3, "statuses_count": 3,
"url": "https://pleroma.example.org/users/user", "url": "https://pleroma.example.org/users/user",
"username": "user" "username": "user"
@ -366,6 +369,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
"pleroma": {}, "pleroma": {},
"sensitive": false "sensitive": false
}, },
"tags": ["force_unlisted"],
"statuses_count": 1, "statuses_count": 1,
"url": "https://pleroma.example.org/users/lain", "url": "https://pleroma.example.org/users/lain",
"username": "lain" "username": "lain"

View file

@ -207,7 +207,7 @@ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --
# Add it to the daily cron # Add it to the daily cron
echo '#!/bin/sh echo '#!/bin/sh
certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook "systemctl reload nginx" certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx"
' > /etc/cron.daily/renew-pleroma-cert ' > /etc/cron.daily/renew-pleroma-cert
chmod +x /etc/cron.daily/renew-pleroma-cert chmod +x /etc/cron.daily/renew-pleroma-cert
@ -228,7 +228,7 @@ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --
# Add it to the daily cron # Add it to the daily cron
echo '#!/bin/sh echo '#!/bin/sh
certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook "rc-service nginx reload" certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "rc-service nginx reload"
' > /etc/periodic/daily/renew-pleroma-cert ' > /etc/periodic/daily/renew-pleroma-cert
chmod +x /etc/periodic/daily/renew-pleroma-cert chmod +x /etc/periodic/daily/renew-pleroma-cert

View file

@ -149,7 +149,7 @@ def run(["gen" | rest]) do
uploads_dir = uploads_dir =
get_option( get_option(
options, options,
:upload_dir, :uploads_dir,
"What directory should media uploads go in (when using the local uploader)?", "What directory should media uploads go in (when using the local uploader)?",
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads]) Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
) )

View file

@ -836,15 +836,12 @@ def unblock(blocker, %{ap_id: ap_id}) do
def mutes?(nil, _), do: false 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.info.mutes, ap_id)
def blocks?(user, %{ap_id: ap_id}) do def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
blocks = user.info.blocks blocks = info.blocks
domain_blocks = user.info.domain_blocks domain_blocks = info.domain_blocks
%{host: host} = URI.parse(ap_id) %{host: host} = URI.parse(ap_id)
Enum.member?(blocks, ap_id) || Enum.member?(blocks, ap_id) || Enum.any?(domain_blocks, &(&1 == host))
Enum.any?(domain_blocks, fn domain ->
host == domain
end)
end end
def subscribed_to?(user, %{ap_id: ap_id}) do def subscribed_to?(user, %{ap_id: ap_id}) do

View file

@ -43,6 +43,8 @@ def search(query_string, opts \\ []) do
defp search_query(query_string, for_user, following) do defp search_query(query_string, for_user, following) do
for_user for_user
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user)
|> filter_blocked_domains(for_user)
|> search_subqueries(query_string) |> search_subqueries(query_string)
|> union_subqueries |> union_subqueries
|> distinct_query() |> distinct_query()
@ -55,6 +57,25 @@ defp search_query(query_string, for_user, following) do
defp base_query(_user, false), do: User defp base_query(_user, false), do: User
defp base_query(user, true), do: User.get_followers_query(user) defp base_query(user, true), do: User.get_followers_query(user)
defp filter_blocked_user(query, %User{info: %{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}})
when length(domain_blocks) > 0 do
domains = Enum.join(domain_blocks, ",")
from(
q in query,
where: fragment("substring(ap_id from '.*://([^/]*)') NOT IN (?)", ^domains)
)
end
defp filter_blocked_domains(query, _), do: query
defp paginate(query, limit, offset) do defp paginate(query, limit, offset) do
from(q in query, limit: ^limit, offset: ^offset) from(q in query, limit: ^limit, offset: ^offset)
end end

View file

@ -46,8 +46,10 @@ def render("show.json", %{report: report}) do
} }
end end
defp merge_account_views(user) do defp merge_account_views(%User{} = user) do
Pleroma.Web.MastodonAPI.AccountView.render("account.json", %{user: user}) Pleroma.Web.MastodonAPI.AccountView.render("account.json", %{user: user})
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})) |> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
end end
defp merge_account_views(_), do: %{}
end end

View file

@ -17,8 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search]) plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user)) accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = Activity.search(user, query) statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags_path = Web.base_url() <> "/tag/" tags_path = Web.base_url() <> "/tag/"
tags = tags =
@ -40,8 +40,8 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
end end
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user)) accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = Activity.search(user, query) statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags = tags =
query query
@ -76,4 +76,14 @@ defp search_options(params, user) do
for_user: user for_user: user
] ]
end end
defp with_fallback(f, fallback) do
try do
f.()
rescue
error ->
Logger.error("#{__MODULE__} search error: #{inspect(error)}")
fallback
end
end
end end

View file

@ -162,7 +162,8 @@ def raw_nodeinfo do
accountActivationRequired: Config.get([:instance, :account_activation_required], false), accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Config.get([:instance, :invites_enabled], false), invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features, features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]) restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
} }
} }
end end

55
mix.exs
View file

@ -174,10 +174,14 @@ defp aliases do
# Builds a version string made of: # Builds a version string made of:
# * the application version # * the application version
# * a pre-release if ahead of the tag: the describe string (-count-commithash) # * a pre-release if ahead of the tag: the describe string (-count-commithash)
# * build info: # * branch name
# * build metadata:
# * a build name if `PLEROMA_BUILD_NAME` or `:pleroma, :build_name` is defined # * a build name if `PLEROMA_BUILD_NAME` or `:pleroma, :build_name` is defined
# * the mix environment if different than prod # * the mix environment if different than prod
defp version(version) do defp version(version) do
identifier_filter = ~r/[^0-9a-z\-]+/i
# Pre-release version, denoted from patch version with a hyphen
{git_tag, git_pre_release} = {git_tag, git_pre_release} =
with {tag, 0} <- with {tag, 0} <-
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true), System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
@ -198,6 +202,19 @@ defp version(version) do
) )
end end
# Branch name as pre-release version component, denoted with a dot
branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
true <- branch_name != "master" do
branch_name =
branch_name
|> String.trim()
|> String.replace(identifier_filter, "-")
"." <> branch_name
end
build_name = build_name =
cond do cond do
name = Application.get_env(:pleroma, :build_name) -> name name = Application.get_env(:pleroma, :build_name) -> name
@ -206,28 +223,26 @@ defp version(version) do
end end
env_name = if Mix.env() != :prod, do: to_string(Mix.env()) env_name = if Mix.env() != :prod, do: to_string(Mix.env())
env_override = System.get_env("PLEROMA_BUILD_ENV")
build = env_name =
[build_name, env_name] case env_override do
|> Enum.filter(fn string -> string && string != "" end) nil -> env_name
|> Enum.join("-") env_override when env_override in ["", "prod"] -> nil
|> (fn env_override -> env_override
"" -> nil
string -> "+" <> string
end).()
branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
true <- branch_name != "master" do
branch_name =
String.trim(branch_name)
|> String.replace(~r/[^0-9a-z\-\.]+/i, "-")
"-" <> branch_name
end end
[version, git_pre_release, branch_name, build] # Build metadata, denoted with a plus sign
build_metadata =
[build_name, env_name]
|> Enum.filter(fn string -> string && string != "" end)
|> Enum.join(".")
|> (fn
"" -> nil
string -> "+" <> String.replace(string, identifier_filter, "-")
end).()
[version, git_pre_release, branch_name, build_metadata]
|> Enum.filter(fn string -> string && string != "" end) |> Enum.filter(fn string -> string && string != "" end)
|> Enum.join() |> Enum.join()
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.User do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:users) do create_if_not_exists table(:users) do
add :email, :string add :email, :string
add :password_hash, :string add :password_hash, :string
add :name, :string add :name, :string

View file

@ -2,13 +2,13 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Activity do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:activities) do create_if_not_exists table(:activities) do
add :data, :map add :data, :map
timestamps() timestamps()
end end
create index(:activities, [:data], using: :gin) create_if_not_exists index(:activities, [:data], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Object do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:objects) do create_if_not_exists table(:objects) do
add :data, :map add :data, :map
timestamps() timestamps()

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexToObjects do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:objects, [:data], using: :gin) create_if_not_exists index(:objects, [:data], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToEmailAndNickname do
use Ecto.Migration use Ecto.Migration
def change do def change do
create unique_index(:users, [:email]) create_if_not_exists unique_index(:users, [:email])
create unique_index(:users, [:nickname]) create_if_not_exists unique_index(:users, [:nickname])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubServerSubscription do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:websub_server_subscriptions) do create_if_not_exists table(:websub_server_subscriptions) do
add :topic, :string add :topic, :string
add :callback, :string add :callback, :string
add :secret, :string add :secret, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubClientSubscription do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:websub_client_subscriptions) do create_if_not_exists table(:websub_client_subscriptions) do
add :topic, :string add :topic, :string
add :secret, :string add :secret, :string
add :valid_until, :naive_datetime_usec add :valid_until, :naive_datetime_usec

View file

@ -1,10 +1,12 @@
defmodule Pleroma.Repo.Migrations.AddIdContraintsToActivitiesAndObjectsPartTwo do defmodule Pleroma.Repo.Migrations.AddIdContraintsToActivitiesAndObjectsPartTwo do
use Ecto.Migration use Ecto.Migration
def change do def up do
drop_if_exists index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index) drop_if_exists index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index)
drop_if_exists index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index) drop_if_exists index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index)
create unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index) create_if_not_exists unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index)
create unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index) create_if_not_exists unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index)
end end
def down, do: :ok
end end

View file

@ -6,6 +6,6 @@ def change do
add :local, :boolean, default: true add :local, :boolean, default: true
end end
create index(:activities, [:local]) create_if_not_exists index(:activities, [:local])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAPID do
use Ecto.Migration use Ecto.Migration
def change do def change do
create unique_index(:users, [:ap_id]) create_if_not_exists unique_index(:users, [:ap_id])
end end
end end

View file

@ -1,19 +1,31 @@
defmodule Pleroma.Repo.Migrations.CaseInsensivtivity do defmodule Pleroma.Repo.Migrations.CaseInsensivtivity do
use Ecto.Migration use Ecto.Migration
# Two-steps alters are intentional.
# When alter of 2 columns is done in a single operation,
# inconsistent failures happen because of index on `email` column.
def up do def up do
execute ("create extension if not exists citext") execute("create extension if not exists citext")
alter table(:users) do alter table(:users) do
modify :email, :citext modify(:email, :citext)
modify :nickname, :citext end
alter table(:users) do
modify(:nickname, :citext)
end end
end end
def down do def down do
alter table(:users) do alter table(:users) do
modify :email, :string modify(:email, :string)
modify :nickname, :string
end end
execute ("drop extension if exists citext")
alter table(:users) do
modify(:nickname, :string)
end
execute("drop extension if exists citext")
end end
end end

View file

@ -1,9 +1,16 @@
defmodule Pleroma.Repo.Migrations.LongerBios do defmodule Pleroma.Repo.Migrations.LongerBios do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:users) do alter table(:users) do
modify :bio, :text modify :bio, :text
end end
end end
def down do
alter table(:users) do
modify :bio, :string
end
end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.RemoveActivitiesIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
drop index(:activities, [:data]) drop_if_exists index(:activities, [:data])
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddObjectActivityIndex do
def change do def change do
# This was wrong, now a noop # This was wrong, now a noop
# create index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) # create_if_not_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddObjectActivityIndexPartTwo do
def change do def change do
drop_if_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) drop_if_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
create index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) create_if_not_exists index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddActorIndexToActivity do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index) create_if_not_exists index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddMastodonApps do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:apps) do create_if_not_exists table(:apps) do
add :client_name, :string add :client_name, :string
add :redirect_uris, :string add :redirect_uris, :string
add :scopes, :string add :scopes, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthAuthorizations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:oauth_authorizations) do create_if_not_exists table(:oauth_authorizations) do
add :app_id, references(:apps) add :app_id, references(:apps)
add :user_id, references(:users) add :user_id, references(:users)
add :token, :string add :token, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthToken do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:oauth_tokens) do create_if_not_exists table(:oauth_tokens) do
add :app_id, references(:apps) add :app_id, references(:apps)
add :user_id, references(:users) add :user_id, references(:users)
add :token, :string add :token, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateNotifications do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:notifications) do create_if_not_exists table(:notifications) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :activity_id, references(:activities, on_delete: :delete_all) add :activity_id, references(:activities, on_delete: :delete_all)
add :seen, :boolean, default: false add :seen, :boolean, default: false
@ -10,6 +10,6 @@ def change do
timestamps() timestamps()
end end
create index(:notifications, [:user_id]) create_if_not_exists index(:notifications, [:user_id])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePasswordResetTokens do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:password_reset_tokens) do create_if_not_exists table(:password_reset_tokens) do
add :token, :string add :token, :string
add :user_id, references(:users) add :user_id, references(:users)
add :used, :boolean, default: false add :used, :boolean, default: false

View file

@ -12,7 +12,7 @@ def up do
end end
def down do def down do
drop index(:activities, [:actor, "id DESC NULLS LAST"]) drop_if_exists index(:activities, [:actor, "id DESC NULLS LAST"])
alter table(:activities) do alter table(:activities) do
remove :actor remove :actor
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddLocalIndexToUser do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, [:local]) create_if_not_exists index(:users, [:local])
end end
end end

View file

@ -6,6 +6,6 @@ def change do
add :recipients, {:array, :string} add :recipients, {:array, :string}
end end
create index(:activities, [:recipients], using: :gin) create_if_not_exists index(:activities, [:recipients], using: :gin)
end end
end end

View file

@ -18,4 +18,6 @@ def up do
end) end)
end end
end end
def down, do: :ok
end end

View file

@ -1,7 +1,7 @@
defmodule Pleroma.Repo.Migrations.MakeFollowingPostgresArray do defmodule Pleroma.Repo.Migrations.MakeFollowingPostgresArray do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:users) do alter table(:users) do
add :following_temp, {:array, :string} add :following_temp, {:array, :string}
end end
@ -15,4 +15,6 @@ def change do
end end
rename table(:users), :following_temp, to: :following rename table(:users), :following_temp, to: :following
end end
def down, do: :ok
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.DropLocalIndexOnActivities do
use Ecto.Migration use Ecto.Migration
def change do def change do
drop index(:users, [:local]) drop_if_exists index(:users, [:local])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.ActuallyDropLocalIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, [:local]) create_if_not_exists index(:users, [:local])
drop_if_exists index("activities", :local) drop_if_exists index("activities", :local)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateLists do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:lists) do create_if_not_exists table(:lists) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :title, :string add :title, :string
add :following, {:array, :string} add :following, {:array, :string}
@ -10,6 +10,6 @@ def change do
timestamps() timestamps()
end end
create index(:lists, [:user_id]) create_if_not_exists index(:lists, [:user_id])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.CreateUserTrigramIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) create_if_not_exists index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddListFollowIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:lists, [:following]) create_if_not_exists index(:lists, [:following])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateUserInviteTokens do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:user_invite_tokens) do create_if_not_exists table(:user_invite_tokens) do
add :token, :string add :token, :string
add :used, :boolean, default: false add :used, :boolean, default: false

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateFilters do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:filters) do create_if_not_exists table(:filters) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :filter_id, :integer add :filter_id, :integer
add :hide, :boolean add :hide, :boolean
@ -14,7 +14,7 @@ def change do
timestamps() timestamps()
end end
create index(:filters, [:user_id]) create_if_not_exists index(:filters, [:user_id])
create index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index) create_if_not_exists index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index)
end end
end end

View file

@ -7,7 +7,7 @@ def change do
add :recipients_cc, {:array, :string} add :recipients_cc, {:array, :string}
end end
create index(:activities, [:recipients_to], using: :gin) create_if_not_exists index(:activities, [:recipients_to], using: :gin)
create index(:activities, [:recipients_cc], using: :gin) create_if_not_exists index(:activities, [:recipients_cc], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.ActivitiesAddToCcIndices do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin) create_if_not_exists index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin)
create index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin) create_if_not_exists index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin)
end end
end end

View file

@ -1,10 +1,17 @@
defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:activities) do alter table(:activities) do
remove :recipients_to remove :recipients_to
remove :recipients_cc remove :recipients_cc
end end
end end
def down do
alter table(:activities) do
add :recipients_to, {:array, :string}
add :recipients_cc, {:array, :string}
end
end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsModeratorIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin) create_if_not_exists index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table("push_subscriptions") do create_if_not_exists table("push_subscriptions") do
add :user_id, references("users", on_delete: :delete_all) add :user_id, references("users", on_delete: :delete_all)
add :token_id, references("oauth_tokens", on_delete: :delete_all) add :token_id, references("oauth_tokens", on_delete: :delete_all)
add :endpoint, :string add :endpoint, :string
@ -13,6 +13,6 @@ def change do
timestamps() timestamps()
end end
create index("push_subscriptions", [:user_id, :token_id], unique: true) create_if_not_exists index("push_subscriptions", [:user_id, :token_id], unique: true)
end end
end end

View file

@ -1,7 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddUUIDExtension do defmodule Pleroma.Repo.Migrations.AddUUIDExtension do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute("create extension if not exists \"uuid-ossp\"") execute("create extension if not exists \"uuid-ossp\"")
end end
def down, do: :ok
end end

View file

@ -1,7 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddUUIDsToUserInfo do defmodule Pleroma.Repo.Migrations.AddUUIDsToUserInfo do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute("update users set info = jsonb_set(info, '{\"id\"}', to_jsonb(uuid_generate_v4()))") execute("update users set info = jsonb_set(info, '{\"id\"}', to_jsonb(uuid_generate_v4()))")
end end
def down, do: :ok
end end

View file

@ -6,6 +6,6 @@ def change do
add :tags, {:array, :string} add :tags, {:array, :string}
end end
create index(:users, [:tags], using: :gin) create_if_not_exists index(:users, [:tags], using: :gin)
end end
end end

View file

@ -12,7 +12,7 @@ defmodule Pleroma.Repo.Migrations.UsersAndActivitiesFlakeId do
# 4- update relation pkeys with the new ids # 4- update relation pkeys with the new ids
# 5- rename the temporary column to id # 5- rename the temporary column to id
# 6- re-create the constraints # 6- re-create the constraints
def change do def up do
# Old serial int ids are transformed to 128bits with extra padding. # Old serial int ids are transformed to 128bits with extra padding.
# The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility # The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility
# with previously issued ids. # with previously issued ids.
@ -75,6 +75,8 @@ def change do
stop_clippy_heartbeats(clippy) stop_clippy_heartbeats(clippy)
end end
def down, do: :ok
defp start_clippy_heartbeats() do defp start_clippy_heartbeats() do
count = from(a in "activities", select: count(a.id)) |> Repo.one! count = from(a in "activities", select: count(a.id)) |> Repo.one!

View file

@ -37,12 +37,12 @@ def up do
end end
def down do def down do
drop( drop_if_exists(
index(:activities, ["activity_visibility(actor, recipients, data)"], index(:activities, ["activity_visibility(actor, recipients, data)"],
name: :activities_visibility_index name: :activities_visibility_index
) )
) )
execute("drop function activity_visibility(actor varchar, recipients varchar[], data jsonb)") execute("drop function if exists activity_visibility(actor varchar, recipients varchar[], data jsonb)")
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateUserFtsIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index( create_if_not_exists index(
:users, :users,
[ [
""" """

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Repo.Migrations.FixUserTrigramIndex do
def up do def up do
drop_if_exists(index(:users, [], name: :users_trigram_index)) drop_if_exists(index(:users, [], name: :users_trigram_index))
create( create_if_not_exists(
index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"], index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"],
name: :users_trigram_index, name: :users_trigram_index,
using: :gist using: :gist
@ -15,7 +15,7 @@ def up do
def down do def down do
drop_if_exists(index(:users, [], name: :users_trigram_index)) drop_if_exists(index(:users, [], name: :users_trigram_index))
create( create_if_not_exists(
index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
) )
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsAdminIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin)) create_if_not_exists(index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin))
end end
end end

View file

@ -2,14 +2,14 @@ defmodule Pleroma.Repo.Migrations.CreateInstances do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:instances) do create_if_not_exists table(:instances) do
add :host, :string add :host, :string
add :unreachable_since, :naive_datetime_usec add :unreachable_since, :naive_datetime_usec
timestamps() timestamps()
end end
create unique_index(:instances, [:host]) create_if_not_exists unique_index(:instances, [:host])
create index(:instances, [:unreachable_since]) create_if_not_exists index(:instances, [:unreachable_since])
end end
end end

View file

@ -1,9 +1,11 @@
defmodule Pleroma.Repo.Migrations.FixInfoIds do defmodule Pleroma.Repo.Migrations.FixInfoIds do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute( execute(
"update users set info = jsonb_set(info, '{id}', to_jsonb(uuid_generate_v4())) where info->'id' is null;" "update users set info = jsonb_set(info, '{id}', to_jsonb(uuid_generate_v4())) where info->'id' is null;"
) )
end end
def down, do: :ok
end end

View file

@ -1,9 +1,15 @@
defmodule Pleroma.Repo.Migrations.ChangePushSubscriptionsVarchar do defmodule Pleroma.Repo.Migrations.ChangePushSubscriptionsVarchar do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:push_subscriptions) do alter table(:push_subscriptions) do
modify(:endpoint, :varchar) modify(:endpoint, :varchar)
end end
end end
def down do
alter table(:push_subscriptions) do
modify(:endpoint, :string)
end
end
end end

View file

@ -19,7 +19,7 @@ def up do
end end
def down do def down do
drop( drop_if_exists(
index(:activities, ["activity_visibility(actor, recipients, data)", "id DESC"], index(:activities, ["activity_visibility(actor, recipients, data)", "id DESC"],
name: :activities_visibility_index, name: :activities_visibility_index,
concurrently: true, concurrently: true,

View file

@ -2,11 +2,11 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:thread_mutes) do create_if_not_exists table(:thread_mutes) do
add :user_id, references(:users, type: :uuid, on_delete: :delete_all) add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
add :context, :string add :context, :string
end end
create unique_index(:thread_mutes, [:user_id, :context], name: :unique_index) create_if_not_exists unique_index(:thread_mutes, [:user_id, :context], name: :unique_index)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateRegistrations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:registrations, primary_key: false) do create_if_not_exists table(:registrations, primary_key: false) do
add :id, :uuid, primary_key: true add :id, :uuid, primary_key: true
add :user_id, references(:users, type: :uuid, on_delete: :delete_all) add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
add :provider, :string add :provider, :string
@ -12,7 +12,7 @@ def change do
timestamps() timestamps()
end end
create unique_index(:registrations, [:provider, :uid]) create_if_not_exists unique_index(:registrations, [:provider, :uid])
create unique_index(:registrations, [:user_id, :provider, :uid]) create_if_not_exists unique_index(:registrations, [:user_id, :provider, :uid])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.CreateNotificationIdIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:notifications, ["id desc nulls last"]) create_if_not_exists index(:notifications, ["id desc nulls last"])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateScheduledActivities do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:scheduled_activities) do create_if_not_exists table(:scheduled_activities) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:scheduled_at, :naive_datetime, null: false) add(:scheduled_at, :naive_datetime, null: false)
add(:params, :map, null: false) add(:params, :map, null: false)
@ -10,7 +10,7 @@ def change do
timestamps() timestamps()
end end
create(index(:scheduled_activities, [:scheduled_at])) create_if_not_exists(index(:scheduled_activities, [:scheduled_at]))
create(index(:scheduled_activities, [:user_id])) create_if_not_exists(index(:scheduled_activities, [:user_id]))
end end
end end

View file

@ -2,8 +2,8 @@ defmodule Pleroma.Repo.Migrations.AddOauthTokenIndexes do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(unique_index(:oauth_tokens, [:token])) create_if_not_exists(unique_index(:oauth_tokens, [:token]))
create(index(:oauth_tokens, [:app_id])) create_if_not_exists(index(:oauth_tokens, [:app_id]))
create(index(:oauth_tokens, [:user_id])) create_if_not_exists(index(:oauth_tokens, [:user_id]))
end end
end end

View file

@ -1,6 +1,6 @@
defmodule Pleroma.Repo.Migrations.AddIndexOnSubscribers do defmodule Pleroma.Repo.Migrations.AddIndexOnSubscribers do
use Ecto.Migration use Ecto.Migration
@disable_ddl_transaction true @disable_ddl_transaction true
def change do def change do
create index(:users, ["(info->'subscribers')"], name: :users_subscribers_index, using: :gin, concurrently: true) create index(:users, ["(info->'subscribers')"], name: :users_subscribers_index, using: :gin, concurrently: true)

View file

@ -6,12 +6,12 @@ defmodule Pleroma.Repo.Migrations.CreateConversations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:conversations) do create_if_not_exists table(:conversations) do
add(:ap_id, :string, null: false) add(:ap_id, :string, null: false)
timestamps() timestamps()
end end
create table(:conversation_participations) do create_if_not_exists table(:conversation_participations) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:conversation_id, references(:conversations, on_delete: :delete_all)) add(:conversation_id, references(:conversations, on_delete: :delete_all))
add(:read, :boolean, default: false) add(:read, :boolean, default: false)
@ -19,8 +19,8 @@ def change do
timestamps() timestamps()
end end
create index(:conversation_participations, [:conversation_id]) create_if_not_exists index(:conversation_participations, [:conversation_id])
create unique_index(:conversation_participations, [:user_id, :conversation_id]) create_if_not_exists unique_index(:conversation_participations, [:user_id, :conversation_id])
create unique_index(:conversations, [:ap_id]) create_if_not_exists unique_index(:conversations, [:ap_id])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddParticipationUpdatedAtIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:conversation_participations, ["updated_at desc"]) create_if_not_exists index(:conversation_participations, ["updated_at desc"])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnUserInfoDeactivated do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin)) create_if_not_exists(index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin))
end end
end end

View file

@ -2,13 +2,13 @@ defmodule Pleroma.Repo.Migrations.CreateBookmarks do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:bookmarks) do create_if_not_exists table(:bookmarks) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all)) add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all))
timestamps() timestamps()
end end
create(unique_index(:bookmarks, [:user_id, :activity_id])) create_if_not_exists(unique_index(:bookmarks, [:user_id, :activity_id]))
end end
end end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Repo alias Pleroma.Repo
def change do def up do
query = query =
from(u in User, from(u in User,
where: u.local == true, where: u.local == true,
@ -18,7 +18,7 @@ def change do
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} -> |> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
Enum.each(bookmarks, fn ap_id -> Enum.each(bookmarks, fn ap_id ->
activity = Activity.get_create_by_object_ap_id(ap_id) activity = Activity.get_create_by_object_ap_id(ap_id)
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id) unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
end) end)
end) end)
@ -26,4 +26,10 @@ def change do
remove(:bookmarks) remove(:bookmarks)
end end
end end
def down do
alter table(:users) do
add :bookmarks, {:array, :string}, null: false, default: []
end
end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddFTSIndexToObjects do
def change do def change do
drop_if_exists index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], using: :gin, name: :activities_fts) drop_if_exists index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], using: :gin, name: :activities_fts)
create index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddRefreshTokenIndexToToken do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(unique_index(:oauth_tokens, [:refresh_token])) create_if_not_exists(unique_index(:oauth_tokens, [:refresh_token]))
end end
end end

View file

@ -1,9 +1,15 @@
defmodule Pleroma.Repo.Migrations.ChangeHideColumnInFilterTable do defmodule Pleroma.Repo.Migrations.ChangeHideColumnInFilterTable do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:filters) do alter table(:filters) do
modify :hide, :boolean, default: false modify :hide, :boolean, default: false
end end
end end
def down do
alter table(:filters) do
modify :hide, :boolean
end
end
end end

View file

@ -68,6 +68,6 @@ def up do
end end
def down do def down do
execute("drop function thread_visibility(actor varchar, activity_id varchar)") execute("drop function if exists thread_visibility(actor varchar, activity_id varchar)")
end end
end end

View file

@ -2,12 +2,12 @@ defmodule Pleroma.Repo.Migrations.CreateConfig do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:config) do create_if_not_exists table(:config) do
add(:key, :string) add(:key, :string)
add(:value, :binary) add(:value, :binary)
timestamps() timestamps()
end end
create(unique_index(:config, :key)) create_if_not_exists(unique_index(:config, :key))
end end
end end

View file

@ -1,10 +1,12 @@
defmodule Pleroma.Repo.Migrations.AddNonFollowsAndNonFollowersFieldsToNotificationSettings do defmodule Pleroma.Repo.Migrations.AddNonFollowsAndNonFollowersFieldsToNotificationSettings do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute(""" execute("""
update users set info = jsonb_set(info, '{notification_settings}', '{"local": true, "remote": true, "follows": true, "followers": true, "non_follows": true, "non_followers": true}') update users set info = jsonb_set(info, '{notification_settings}', '{"local": true, "remote": true, "follows": true, "followers": true, "non_follows": true, "non_followers": true}')
where local=true where local=true
""") """)
end end
def down, do: :ok
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnActivitiesLocal do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index("activities", [:local])) create_if_not_exists(index("activities", [:local]))
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddTagIndexToObjects do
def change do def change do
drop_if_exists index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags) drop_if_exists index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags)
create index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags) create_if_not_exists index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags)
end end
end end

View file

@ -6,7 +6,7 @@ def change do
add(:group, :string) add(:group, :string)
end end
drop(unique_index("config", :key)) drop_if_exists(unique_index("config", :key))
create(unique_index("config", [:group, :key])) create_if_not_exists(unique_index("config", [:group, :key]))
end end
end end

View file

@ -14,7 +14,7 @@ def up do
return new; return new;
end end
$$ LANGUAGE plpgsql") $$ LANGUAGE plpgsql")
execute("create index objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');") execute("create index if not exists objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');")
execute("CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON objects execute("CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON objects
FOR EACH ROW EXECUTE PROCEDURE objects_fts_update()") FOR EACH ROW EXECUTE PROCEDURE objects_fts_update()")
@ -23,12 +23,12 @@ def up do
end end
def down do def down do
execute "drop index objects_fts" execute "drop index if exists objects_fts"
execute "drop trigger tsvectorupdate on objects" execute "drop trigger if exists tsvectorupdate on objects"
execute "drop function objects_fts_update()" execute "drop function if exists objects_fts_update()"
alter table(:objects) do alter table(:objects) do
remove(:fts_content, :tsvector) remove(:fts_content, :tsvector)
end end
create index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts)
end end
end end

View file

@ -30,12 +30,15 @@ detect_flavour() {
detect_branch() { detect_branch() {
version="$(cut -d' ' -f2 <"$RELEASE_ROOT"/releases/start_erl.data)" version="$(cut -d' ' -f2 <"$RELEASE_ROOT"/releases/start_erl.data)"
branch="$(echo "$version" | cut -d'-' -f 4)" # Expected format: major.minor.patch_version(-number_of_commits_ahead_of_tag-gcommit_hash).branch
branch="$(echo "$version" | cut -d'.' -f 4)"
if [ "$branch" = "develop" ]; then if [ "$branch" = "develop" ]; then
echo "develop" echo "develop"
elif [ "$branch" = "" ]; then elif [ "$branch" = "" ]; then
echo "master" echo "master"
else else
# Note: branch name in version is of SemVer format and may only contain [0-9a-zA-Z-] symbols —
# if supporting releases for more branches, need to ensure they contain only these symbols.
echo "Releases are built only for master and develop branches" >&2 echo "Releases are built only for master and develop branches" >&2
exit 1 exit 1
fi fi

View file

@ -9,6 +9,12 @@ defmodule Pleroma.Tests.Helpers do
defmacro __using__(_opts) do defmacro __using__(_opts) do
quote do quote do
def collect_ids(collection) do
collection
|> Enum.map(& &1.id)
|> Enum.sort()
end
def refresh_record(%{id: id, __struct__: model} = _), def refresh_record(%{id: id, __struct__: model} = _),
do: refresh_record(model, %{id: id}) do: refresh_record(model, %{id: id})

221
test/user_search_test.exs Normal file
View file

@ -0,0 +1,221 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserSearchTest do
alias Pleroma.Repo
alias Pleroma.User
use Pleroma.DataCase
import Pleroma.Factory
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
describe "User.search" do
test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john")) == 5
end
test "accepts offset parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john", limit: 3, offset: 3)) == 2
end
test "finds a user by full or partial nickname" do
user = insert(:user, %{nickname: "john"})
Enum.each(["john", "jo", "j"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds a user by full or partial name" do
user = insert(:user, %{name: "John Doe"})
Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds users, preferring nickname matches over name matches" do
u1 = insert(:user, %{name: "lain", nickname: "nick1"})
u2 = insert(:user, %{nickname: "lain", name: "nick1"})
assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
end
test "finds users, considering density of matched tokens" do
u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
end
test "finds users, ranking by similarity" do
u1 = insert(:user, %{name: "lain"})
_u2 = insert(:user, %{name: "ean"})
u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
end
test "finds users, handling misspelled requests" do
u1 = insert(:user, %{name: "lain"})
assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
end
test "finds users, boosting ranks of friends and followers" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Doe"})
follower = insert(:user, %{name: "Doe"})
friend = insert(:user, %{name: "Doe"})
{:ok, follower} = User.follow(follower, u1)
{:ok, u1} = User.follow(u1, friend)
assert [friend.id, follower.id, u2.id] --
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
end
test "finds followers of user by partial name" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Jimi"})
follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
follower_lizz = insert(:user, %{name: "Lizz Wright"})
friend = insert(:user, %{name: "Jimi"})
{:ok, follower_jimi} = User.follow(follower_jimi, u1)
{:ok, _follower_lizz} = User.follow(follower_lizz, u2)
{:ok, u1} = User.follow(u1, friend)
assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
follower_jimi.id
]
assert User.search("lizz", following: true, for_user: u1) == []
end
test "find local and remote users for authenticated users" do
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search(for_user: u1)
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
end
test "find only local users for unauthenticated users" do
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
end
test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
Pleroma.Config.put([:instance, :limit_to_local_content], false)
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search()
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "finds a user whose name is nil" do
_user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
assert user_two ==
User.search("lain@pleroma.soykaf.com")
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end
test "does not yield false-positive matches" do
insert(:user, %{name: "John Doe"})
Enum.each(["mary", "a", ""], fn query ->
assert [] == User.search(query)
end)
end
test "works with URIs" do
user = insert(:user)
results =
User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
result = results |> List.first()
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
assert length(results) == 1
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
end
test "excludes a blocked users from search result" do
user = insert(:user, %{nickname: "Bill"})
[blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"}))
blocked_user2 =
insert(
:user,
%{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"}
)
User.block_domain(user, "awful-and-rude-instance.com")
User.block(user, blocked_user)
account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids
assert account_ids == collect_ids(users)
refute Enum.member?(account_ids, blocked_user.id)
refute Enum.member?(account_ids, blocked_user2.id)
assert length(account_ids) == 3
end
end
end

View file

@ -1012,189 +1012,6 @@ test "User.delete() plugs any possible zombie objects" do
end end
end end
describe "User.search" do
test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john")) == 5
end
test "accepts offset parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john", limit: 3, offset: 3)) == 2
end
test "finds a user by full or partial nickname" do
user = insert(:user, %{nickname: "john"})
Enum.each(["john", "jo", "j"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds a user by full or partial name" do
user = insert(:user, %{name: "John Doe"})
Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds users, preferring nickname matches over name matches" do
u1 = insert(:user, %{name: "lain", nickname: "nick1"})
u2 = insert(:user, %{nickname: "lain", name: "nick1"})
assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
end
test "finds users, considering density of matched tokens" do
u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
end
test "finds users, ranking by similarity" do
u1 = insert(:user, %{name: "lain"})
_u2 = insert(:user, %{name: "ean"})
u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
end
test "finds users, handling misspelled requests" do
u1 = insert(:user, %{name: "lain"})
assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
end
test "finds users, boosting ranks of friends and followers" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Doe"})
follower = insert(:user, %{name: "Doe"})
friend = insert(:user, %{name: "Doe"})
{:ok, follower} = User.follow(follower, u1)
{:ok, u1} = User.follow(u1, friend)
assert [friend.id, follower.id, u2.id] --
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
end
test "finds followers of user by partial name" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Jimi"})
follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
follower_lizz = insert(:user, %{name: "Lizz Wright"})
friend = insert(:user, %{name: "Jimi"})
{:ok, follower_jimi} = User.follow(follower_jimi, u1)
{:ok, _follower_lizz} = User.follow(follower_lizz, u2)
{:ok, u1} = User.follow(u1, friend)
assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
follower_jimi.id
]
assert User.search("lizz", following: true, for_user: u1) == []
end
test "find local and remote users for authenticated users" do
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search(for_user: u1)
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
end
test "find only local users for unauthenticated users" do
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
end
test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
Pleroma.Config.put([:instance, :limit_to_local_content], false)
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search()
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "finds a user whose name is nil" do
_user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
assert user_two ==
User.search("lain@pleroma.soykaf.com")
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end
test "does not yield false-positive matches" do
insert(:user, %{name: "John Doe"})
Enum.each(["mary", "a", ""], fn query ->
assert [] == User.search(query)
end)
end
test "works with URIs" do
user = insert(:user)
results =
User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
result = results |> List.first()
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
assert length(results) == 1
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
end
end
test "auth_active?/1 works correctly" do test "auth_active?/1 works correctly" do
Pleroma.Config.put([:instance, :account_activation_required], true) Pleroma.Config.put([:instance, :account_activation_required], true)

View file

@ -111,4 +111,20 @@ test "sanitizes report description" do
refute "<script> alert('hecked :D:D:D:D:D:D:D') </script>" == refute "<script> alert('hecked :D:D:D:D:D:D:D') </script>" ==
ReportView.render("show.json", %{report: activity})[:content] ReportView.render("show.json", %{report: activity})[:content]
end end
test "doesn't error out when the user doesn't exists" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} =
CommonAPI.report(user, %{
"account_id" => other_user.id,
"comment" => ""
})
Pleroma.User.delete(other_user)
Pleroma.User.invalidate_cache(other_user)
assert %{} = ReportView.render("show.json", %{report: activity})
end
end end