diff options
190 files changed, 2388 insertions, 585 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cd0561852..b848d3c98 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,13 @@ image: git.pleroma.social:5050/pleroma/pleroma/ci-base variables: &global_variables + # Only used for the release + ELIXIR_VER: 1.12.3 POSTGRES_DB: pleroma_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres DB_HOST: postgres - DB_PORT: 5432 + DB_PORT: "5432" MIX_ENV: test workflow: @@ -189,7 +191,7 @@ unit-testing-rum: lint: extends: .build_changes_policy - image: ¤t_elixir elixir:1.12-alpine + image: ¤t_elixir elixir:1.13-alpine stage: test cache: *testing_cache_policy before_script: ¤t_bfr_script @@ -294,7 +296,7 @@ stop_review_app: amd64: stage: release - image: elixir:1.11.4 + image: elixir:$ELIXIR_VER only: &release-only - stable@pleroma/pleroma - develop@pleroma/pleroma @@ -318,8 +320,9 @@ amd64: - deps variables: &release-variables MIX_ENV: prod + VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS before_script: &before-release - - apt-get update && apt-get install -y cmake libmagic-dev + - apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev - echo "import Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force @@ -334,13 +337,13 @@ amd64-musl: stage: release artifacts: *release-artifacts only: *release-only - image: elixir:1.11.4-alpine + image: elixir:$ELIXIR_VER-alpine tags: - amd64 cache: *release-cache variables: *release-variables before_script: &before-release-musl - - apk add git build-base cmake file-dev openssl + - apk add git build-base cmake file-dev openssl vips-dev - echo "import Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force @@ -352,7 +355,7 @@ arm: only: *release-only tags: - arm32-specified - image: arm32v7/elixir:1.11.4 + image: arm32v7/elixir:$ELIXIR_VER cache: *release-cache variables: *release-variables before_script: *before-release @@ -364,7 +367,7 @@ arm-musl: only: *release-only tags: - arm32-specified - image: arm32v7/elixir:1.11.4-alpine + image: arm32v7/elixir:$ELIXIR_VER-alpine cache: *release-cache variables: *release-variables before_script: *before-release-musl @@ -376,7 +379,7 @@ arm64: only: *release-only tags: - arm - image: arm64v8/elixir:1.11.4 + image: arm64v8/elixir:$ELIXIR_VER cache: *release-cache variables: *release-variables before_script: *before-release @@ -388,7 +391,7 @@ arm64-musl: only: *release-only tags: - arm - image: arm64v8/elixir:1.11.4-alpine + image: arm64v8/elixir:$ELIXIR_VER-alpine cache: *release-cache variables: *release-variables before_script: *before-release-musl diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md index fdf219f99..641d9cfd8 100644 --- a/.gitlab/merge_request_templates/Default.md +++ b/.gitlab/merge_request_templates/Default.md @@ -3,7 +3,7 @@ `<code>` can be anything, but we recommend using a more or less unique identifier to avoid collisions, such as the branch name. - `<type>` can be `add`, `remove`, `fix`, `security` or `skip`. `skip` is only used if there is no user-visible change in the MR (for example, only editing comments in the code). Otherwise, choose a type that corresponds to your change. + `<type>` can be `add`, `change`, `remove`, `fix`, `security` or `skip`. `skip` is only used if there is no user-visible change in the MR (for example, only editing comments in the code). Otherwise, choose a type that corresponds to your change. In the file, write the changelog entry. For example, if an MR adds group functionality, we can create a file named `group.add` and write `Add group functionality` in it. diff --git a/.rgignore b/.rgignore new file mode 100644 index 000000000..975056b6d --- /dev/null +++ b/.rgignore @@ -0,0 +1 @@ +priv/static diff --git a/Dockerfile b/Dockerfile index 72c227b76..69c3509de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG ELIXIR_IMG=hexpm/elixir -ARG ELIXIR_VER=1.11.4 +ARG ELIXIR_VER=1.12.3 ARG ERLANG_VER=24.2.1 ARG ALPINE_VER=3.17.0 @@ -8,8 +8,9 @@ FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as bu COPY . . ENV MIX_ENV=prod +ENV VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS -RUN apk add git gcc g++ musl-dev make cmake file-dev &&\ +RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\ echo "import Config" > config/prod.secret.exs &&\ mix local.hex --force &&\ mix local.rebar --force &&\ @@ -37,7 +38,7 @@ ARG HOME=/opt/pleroma ARG DATA=/var/lib/pleroma RUN apk update &&\ - apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\ + apk add exiftool ffmpeg vips libmagic ncurses postgresql-client &&\ adduser --system --shell /bin/false --home ${HOME} pleroma &&\ mkdir -p ${DATA}/uploads &&\ mkdir -p ${DATA}/static &&\ diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/benchmarks/mix/tasks/pleroma/benchmark.ex index f32492169..42b28478d 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/benchmarks/mix/tasks/pleroma/benchmark.ex @@ -3,8 +3,20 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Benchmark do - import Mix.Pleroma + @shortdoc "Benchmarks" + @moduledoc """ + Benchmark tasks available: + + adapters + render_timeline + search + tag + + MIX_ENV=benchmark mix pleroma.benchmark adapters + """ + use Mix.Task + import Mix.Pleroma def run(["search"]) do start_pleroma() @@ -63,7 +75,7 @@ defmodule Mix.Tasks.Pleroma.Benchmark do Benchee.run( %{ - "Standart rendering" => fn activities -> + "Standard rendering" => fn activities -> Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ activities: activities, for: user, diff --git a/changelog.d/3900.change b/changelog.d/3900.change new file mode 100644 index 000000000..fe0cc2fbf --- /dev/null +++ b/changelog.d/3900.change @@ -0,0 +1 @@ +Update to Phoenix 1.7 diff --git a/changelog.d/3987.fix b/changelog.d/3987.fix new file mode 100644 index 000000000..5d578cc09 --- /dev/null +++ b/changelog.d/3987.fix @@ -0,0 +1 @@ +Remove checking ImageMagick's commands for Pleroma.Upload.Filter.AnalyzeMetadata diff --git a/changelog.d/anonymous-exception-else.fix b/changelog.d/anonymous-exception-else.fix new file mode 100644 index 000000000..38d5d1be5 --- /dev/null +++ b/changelog.d/anonymous-exception-else.fix @@ -0,0 +1 @@ +Fix #strip_report_status_data diff --git a/changelog.d/authorize-interaction.add b/changelog.d/authorize-interaction.add new file mode 100644 index 000000000..8692209e1 --- /dev/null +++ b/changelog.d/authorize-interaction.add @@ -0,0 +1 @@ +Support /authorize-interaction route used by Mastodon
\ No newline at end of file diff --git a/changelog.d/bare_uri_test.skip b/changelog.d/bare_uri_test.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/bare_uri_test.skip diff --git a/changelog.d/benchee.skip b/changelog.d/benchee.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/benchee.skip diff --git a/changelog.d/blurhash.change b/changelog.d/blurhash.change new file mode 100644 index 000000000..4276eb164 --- /dev/null +++ b/changelog.d/blurhash.change @@ -0,0 +1 @@ +Replace eblurhash with rinpatch_blurhash. This also removes a dependency on ImageMagick. diff --git a/changelog.d/build-release-with-local-libvips.skip b/changelog.d/build-release-with-local-libvips.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/build-release-with-local-libvips.skip diff --git a/changelog.d/digest_emails.fix b/changelog.d/digest_emails.fix new file mode 100644 index 000000000..335a24464 --- /dev/null +++ b/changelog.d/digest_emails.fix @@ -0,0 +1 @@ +Fix the processing of email digest jobs. diff --git a/changelog.d/doc-fix.skip b/changelog.d/doc-fix.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/doc-fix.skip diff --git a/changelog.d/docs-max-elixir-erlang.change b/changelog.d/docs-max-elixir-erlang.change new file mode 100644 index 000000000..a58b7fc17 --- /dev/null +++ b/changelog.d/docs-max-elixir-erlang.change @@ -0,0 +1 @@ +- Document maximum supported version of Erlang & Elixir diff --git a/changelog.d/favicon.add b/changelog.d/favicon.add new file mode 100644 index 000000000..cf12395e7 --- /dev/null +++ b/changelog.d/favicon.add @@ -0,0 +1 @@ +Add support for configuring favicon, embed favicon and PWA manifest in server-generated meta diff --git a/changelog.d/federation_status-access.change b/changelog.d/federation_status-access.change new file mode 100644 index 000000000..952254476 --- /dev/null +++ b/changelog.d/federation_status-access.change @@ -0,0 +1 @@ +- Make `/api/v1/pleroma/federation_status` publicly available diff --git a/changelog.d/fix-dockerfile.skip b/changelog.d/fix-dockerfile.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/fix-dockerfile.skip diff --git a/changelog.d/frontend-management.add b/changelog.d/frontend-management.add new file mode 100644 index 000000000..b85cddd96 --- /dev/null +++ b/changelog.d/frontend-management.add @@ -0,0 +1 @@ +[docs] add frontends management documentation diff --git a/changelog.d/generate-unset-user-keys-migration.skip b/changelog.d/generate-unset-user-keys-migration.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/generate-unset-user-keys-migration.skip diff --git a/changelog.d/healthcheck-disabled-error.fix b/changelog.d/healthcheck-disabled-error.fix new file mode 100644 index 000000000..984384a52 --- /dev/null +++ b/changelog.d/healthcheck-disabled-error.fix @@ -0,0 +1 @@ +TwitterAPI: Return proper error when healthcheck is disabled diff --git a/changelog.d/last_status_at.change b/changelog.d/last_status_at.change new file mode 100644 index 000000000..5417aff30 --- /dev/null +++ b/changelog.d/last_status_at.change @@ -0,0 +1 @@ +- Change AccountView `last_status_at` from a datetime to a date (as done in Mastodon 3.1.0)
\ No newline at end of file diff --git a/changelog.d/loading-order-test-fix.skip b/changelog.d/loading-order-test-fix.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/loading-order-test-fix.skip diff --git a/changelog.d/meilisearch.add b/changelog.d/meilisearch.add new file mode 100644 index 000000000..4856eea2e --- /dev/null +++ b/changelog.d/meilisearch.add @@ -0,0 +1 @@ +Add meilisearch, make search engines pluggable diff --git a/changelog.d/migration-fix.skip b/changelog.d/migration-fix.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/changelog.d/migration-fix.skip @@ -0,0 +1 @@ + diff --git a/changelog.d/no-async-with-clear-config.skip b/changelog.d/no-async-with-clear-config.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/no-async-with-clear-config.skip diff --git a/changelog.d/optimistic-inbox.change b/changelog.d/optimistic-inbox.change new file mode 100644 index 000000000..2cf1ce92c --- /dev/null +++ b/changelog.d/optimistic-inbox.change @@ -0,0 +1 @@ +Optimistic Inbox reduces the processing overhead of incoming activities without instantly verifiable signatures. diff --git a/changelog.d/promex.change b/changelog.d/promex.change new file mode 100644 index 000000000..6c1571c54 --- /dev/null +++ b/changelog.d/promex.change @@ -0,0 +1 @@ +Change the prometheus library to PromEx. diff --git a/changelog.d/quotes-count.skip b/changelog.d/quotes-count.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/quotes-count.skip diff --git a/changelog.d/reachability.change b/changelog.d/reachability.change new file mode 100644 index 000000000..06f63272b --- /dev/null +++ b/changelog.d/reachability.change @@ -0,0 +1 @@ +Reduce the reachability timestamp update to a single upsert query diff --git a/changelog.d/scrobble-url.add b/changelog.d/scrobble-url.add new file mode 100644 index 000000000..24bdeed89 --- /dev/null +++ b/changelog.d/scrobble-url.add @@ -0,0 +1 @@ +Adds the capability to add a URL to a scrobble (optional field) diff --git a/changelog.d/scrubbers-html4-GtS.add b/changelog.d/scrubbers-html4-GtS.add new file mode 100644 index 000000000..7f99dbb25 --- /dev/null +++ b/changelog.d/scrubbers-html4-GtS.add @@ -0,0 +1 @@ +- scrubbers/default: Add more formatting elements from HTML4 / GoToSocial (acronym, bdo, big, cite, dfn, ins, kbd, q, samp, s, tt, var, wbr) diff --git a/changelog.d/system-cflags.fix b/changelog.d/system-cflags.fix new file mode 100644 index 000000000..84de5ad57 --- /dev/null +++ b/changelog.d/system-cflags.fix @@ -0,0 +1 @@ +- Fix eblurhash and elixir-captcha not using system cflags diff --git a/changelog.d/vips.change b/changelog.d/vips.change new file mode 100644 index 000000000..ee18cd34b --- /dev/null +++ b/changelog.d/vips.change @@ -0,0 +1 @@ +Change mediaproxy previews to use vips to generate thumbnails instead of ImageMagick diff --git a/ci/Dockerfile b/ci/Dockerfile index ca28b7029..a2b566873 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.11.4 +FROM elixir:1.12.3 # Single RUN statement, otherwise intermediate images are created # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run diff --git a/config/config.exs b/config/config.exs index e8ae31542..b884b3514 100644 --- a/config/config.exs +++ b/config/config.exs @@ -110,17 +110,6 @@ config :pleroma, :uri_schemes, "xmpp" ] -websocket_config = [ - path: "/websocket", - serializer: [ - {Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"}, - {Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"} - ], - timeout: 60_000, - transport_log: false, - compress: false -] - # Configures the endpoint config :pleroma, Pleroma.Web.Endpoint, url: [host: "localhost"], @@ -130,10 +119,7 @@ config :pleroma, Pleroma.Web.Endpoint, {:_, [ {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + {:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}} ]} ] ], @@ -185,6 +171,7 @@ config :pleroma, :instance, short_description: "", background_image: "/images/city.jpg", instance_thumbnail: "/instance/thumbnail.jpeg", + favicon: "/favicon.png", limit: 5_000, description_limit: 5_000, remote_limit: 100_000, @@ -360,6 +347,8 @@ config :pleroma, :manifest, icons: [ %{ src: "/static/logo.svg", + sizes: "144x144", + purpose: "any", type: "image/svg+xml" } ], @@ -591,7 +580,8 @@ config :pleroma, Oban, remote_fetcher: 2, attachments_cleanup: 1, new_users_digest: 1, - mute_expire: 5 + mute_expire: 5, + search_indexing: 10 ], plugins: [Oban.Plugins.Pruner], crontab: [ @@ -602,7 +592,8 @@ config :pleroma, Oban, config :pleroma, :workers, retries: [ federator_incoming: 5, - federator_outgoing: 5 + federator_outgoing: 5, + search_indexing: 2 ] config :pleroma, Pleroma.Formatter, @@ -660,12 +651,26 @@ config :pleroma, Pleroma.Emails.UserEmail, config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false -config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, - enabled: false, - auth: false, - ip_whitelist: [], - path: "/api/pleroma/app_metrics", - format: :text +config :pleroma, Pleroma.PromEx, + disabled: false, + manual_metrics_start_delay: :no_delay, + drop_metrics_groups: [], + grafana: [ + host: System.get_env("GRAFANA_HOST", "http://localhost:3000"), + auth_token: System.get_env("GRAFANA_TOKEN"), + upload_dashboards_on_start: false, + folder_name: "BEAM", + annotate_app_lifecycle: true + ], + metrics_server: [ + port: 4021, + path: "/metrics", + protocol: :http, + pool_size: 5, + cowboy_opts: [], + auth_strategy: :none + ], + datasource: "Prometheus" config :pleroma, Pleroma.ScheduledActivity, daily_user_limit: 25, @@ -889,11 +894,19 @@ config :pleroma, Pleroma.User.Backup, config :pleroma, ConcurrentLimiter, [ {Pleroma.Web.RichMedia.Helpers, [max_running: 5, max_waiting: 5]}, - {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]} + {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}, + {Pleroma.Search, [max_running: 30, max_waiting: 50]} ] config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: true +config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch + +config :pleroma, Pleroma.Search.Meilisearch, + url: "http://127.0.0.1:7700/", + private_key: nil, + initial_indexing_chunk_size: 100_000 + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/config/description.exs b/config/description.exs index d18649ae8..27e7f7e9b 100644 --- a/config/description.exs +++ b/config/description.exs @@ -988,6 +988,12 @@ config :pleroma, :config_description, [ suggestions: ["/instance/thumbnail.jpeg"] }, %{ + key: :favicon, + type: {:string, :image}, + description: "Favicon of the instance", + suggestions: ["/favicon.png"] + }, + %{ key: :show_reactions, type: :boolean, description: "Let favourites and emoji reactions be viewed through the API." @@ -3466,5 +3472,48 @@ config :pleroma, :config_description, [ ] } ] + }, + %{ + group: :pleroma, + key: Pleroma.Search, + type: :group, + description: "General search settings.", + children: [ + %{ + key: :module, + type: :keyword, + description: "Selected search module.", + suggestion: [Pleroma.Search.DatabaseSearch, Pleroma.Search.Meilisearch] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Search.Meilisearch, + type: :group, + description: "Meilisearch settings.", + children: [ + %{ + key: :url, + type: :string, + description: "Meilisearch URL.", + suggestion: ["http://127.0.0.1:7700/"] + }, + %{ + key: :private_key, + type: :string, + description: + "Private key for meilisearch authentication, or `nil` to disable private key authentication.", + suggestion: [nil] + }, + %{ + key: :initial_indexing_chunk_size, + type: :int, + description: + "Amount of posts in a batch when running the initial indexing operation. Should probably not be more than 100000" <> + " since there's a limit on maximum insert size", + suggestion: [100_000] + } + ] } ] diff --git a/config/test.exs b/config/test.exs index 78303eb1d..cf407ebf2 100644 --- a/config/test.exs +++ b/config/test.exs @@ -133,10 +133,26 @@ config :pleroma, :side_effects, ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock, logger: Pleroma.LoggerMock +config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch + +config :pleroma, Pleroma.Search.Meilisearch, url: "http://127.0.0.1:7700/", private_key: nil + # Reduce recompilation time # https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects config :phoenix, :plug_init_mode, :runtime +config :pleroma, :config_impl, Pleroma.UnstubbedConfigMock + +config :pleroma, Pleroma.PromEx, disabled: true + +# Mox definitions. Only read during compile time. +config :pleroma, Pleroma.User.Backup, config_impl: Pleroma.UnstubbedConfigMock +config :pleroma, Pleroma.Uploaders.S3, ex_aws_impl: Pleroma.Uploaders.S3.ExAwsMock +config :pleroma, Pleroma.Uploaders.S3, config_impl: Pleroma.UnstubbedConfigMock +config :pleroma, Pleroma.Upload, config_impl: Pleroma.UnstubbedConfigMock +config :pleroma, Pleroma.ScheduledActivity, config_impl: Pleroma.UnstubbedConfigMock +config :pleroma, Pleroma.Web.RichMedia.Helpers, config_impl: Pleroma.StaticStubbedConfigMock + if File.exists?("./config/test.secret.exs") do import_config "test.secret.exs" else diff --git a/docs/administration/frontends-management.md b/docs/administration/frontends-management.md new file mode 100644 index 000000000..f982c4bca --- /dev/null +++ b/docs/administration/frontends-management.md @@ -0,0 +1,71 @@ +# Managing installed frontends + +Pleroma lets you install multiple frontends including multiple versions of same frontend. Right now it's only possible to switch which frontend is the default, but in the future it would be possible for user to select which frontend they prefer to use. + +As of 2.6.0 there are two ways of managing frontends - through PleromaFE's Admin Dashboard (preferred, easier method) or through AdminFE (clunky but also works on versions older than 2.6.0). + +!!! note + Managing frontends through UI requires [in-database configuration](../configuration/howto_database_config.md) to be enabled (default on newer instances but might be off on older ones). + +## How it works + +When installing frontends, it creates a folder in [static directory](../configuration/static_dir.md) that follows this pattern: `/frontends/${front-end name}/${front-end version}/`, puts contents of the built frontend in there. Then when accessing the server backend checks what front-end name and version are set to be default and serves index.html and assets from appropriate path. + +!!! warning + + If you've been putting your frontend build directly into static dir as an antiquated way of serving custom frontend, this system will not work and will still serve the custom index.html you put in there. You can still serve custom frontend builds if you put your build into `/frontends/$name/$version` instead and set the "default frontend" fields appropriately. + +Currently, there is no backup system, i.e. when installing `master` version it _will_ overwrite installed `master` version, for now if you want to keep previous version you should back it up manually, i.e. running `cp -r ./frontends/pleroma-fe/master ./frontends/pleroma-fe/master_old` in your static dir. + +## Managing front-ends through Admin Dashboard + +Open up Admin Dashboard (gauge icon in top bar, same as where link to AdminFE was),__ + +switch to "Front-ends" tab. + +This page is designed to be self-explanatory and easy to use, while avoiding issues and pitfalls of AdminFE, but it's also early in development, everything is subject to change. + +!!! warning + This goes without saying, but if you set default frontend to anything except >2.6.0 version of PleromaFE you'll lose the access to Admin Dashboard and will have to use AdminFE to get it back. See below on how to use AdminFE. + +### Limitations + +Currently the list of available for install frontends is essentially hard-coded in backend's configuration, each providing only one version, with exception for PleromaFE which overrides 'pleroma-fe' to also include `develop` version. There is no way to manually install build with a URL (coming soon) nor add more available frontends to the repository (it's broken). + +There is also no way to tell if there is an update available or not, for now you should watch for [announcements](https://pleroma.social/announcements/) of new PleromaFE stable releases to see if there is new stable version. For `develop` version it's up to you whether you want to follow the development process or just reinstall it periodically hoping for new stuff. + +## Using AdminFE to manage frontends + +Access AdminFE either directly by going to `/pleroma/admin` of your instance or by opening Admin Dashboard and clicking the link at the bottom of the window + + + +Go to Settings -> Frontend. + +### Installing front-ends + +At the very top of the page there's a list of available frontends and button to install custom front-end + +!!! tip + Remember to click "Submit" in bottom right corner to save your changes! + +!!! bug + **Available Frontends** section lets you _install_ frontends but **NOT** update/reinstall them. It's only useful for installing a frontend once. + +Due to aforementioned bug, preferred way of installing frontends in AdminFE is by clicking the "Install another frontend" + +and filling in the fields. Unfortunately AdminFE does not provide the raw data necessary for you to fill those fields, so your best bet is to see what backend returns in browser's devtools or refer to the [source code](https://git.pleroma.social/pleroma/pleroma/-/blob/develop/config/config.exs?ref_type=heads#L742-791). For the most part, only **Name**, **Ref** (i.e. version) and **Build URL** fields are required, although some frontends might also require **Build Directory** to work. + +For pleroma-fe you can use either `master` or `develop` refs, or potentially any ref in GitLab that has artifacts for `build` job, but that's outside scope of this document. + +### Selecting default frontend + +Scroll page waaaaay down, search for "Frontends" section, subtitled "Installed frontends management", change the name and reference of the "Primary" frontend. + + + +!!! danger + If you change "Admin" frontend name/reference you risk losing access to AdminFE as well. + +!!! warning + Don't put anything into the "Available" section as it will break the list of available frontends completely, including the "add another frontend" button. If you accidentally put something in there, click the trashbin icon next to "Available" to reset it and restore the frontends list. diff --git a/docs/assets/admin_dash_location.png b/docs/assets/admin_dash_location.png Binary files differnew file mode 100644 index 000000000..4e1d110e7 --- /dev/null +++ b/docs/assets/admin_dash_location.png diff --git a/docs/assets/frontends_tab.png b/docs/assets/frontends_tab.png Binary files differnew file mode 100644 index 000000000..f7c66adab --- /dev/null +++ b/docs/assets/frontends_tab.png diff --git a/docs/assets/old_adminfe_link.png b/docs/assets/old_adminfe_link.png Binary files differnew file mode 100644 index 000000000..5ea6a486c --- /dev/null +++ b/docs/assets/old_adminfe_link.png diff --git a/docs/assets/primary_frontend_section.png b/docs/assets/primary_frontend_section.png Binary files differnew file mode 100644 index 000000000..14c3de41b --- /dev/null +++ b/docs/assets/primary_frontend_section.png diff --git a/docs/assets/way_to_install_frontends.png b/docs/assets/way_to_install_frontends.png Binary files differnew file mode 100644 index 000000000..a90ff2b5d --- /dev/null +++ b/docs/assets/way_to_install_frontends.png diff --git a/docs/configuration/search.md b/docs/configuration/search.md new file mode 100644 index 000000000..f131948a7 --- /dev/null +++ b/docs/configuration/search.md @@ -0,0 +1,123 @@ +# Configuring search + +{! backend/administration/CLI_tasks/general_cli_task_info.include !} + +## Built-in search + +To use built-in search that has no external dependencies, set the search module to `Pleroma.Activity`: + +> config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch + +While it has no external dependencies, it has problems with performance and relevancy. + +## Meilisearch + +Note that it's quite a bit more memory hungry than PostgreSQL (around 4-5G for ~1.2 million +posts while idle and up to 7G while indexing initially). The disk usage for this additional index is also +around 4 gigabytes. Like [RUM](./cheatsheet.md#rum-indexing-for-full-text-search) indexes, it offers considerably +higher performance and ordering by timestamp in a reasonable amount of time. +Additionally, the search results seem to be more accurate. + +Due to high memory usage, it may be best to set it up on a different machine, if running pleroma on a low-resource +computer, and use private key authentication to secure the remote search instance. + +To use [meilisearch](https://www.meilisearch.com/), set the search module to `Pleroma.Search.Meilisearch`: + +> config :pleroma, Pleroma.Search, module: Pleroma.Search.Meilisearch + +You then need to set the address of the meilisearch instance, and optionally the private key for authentication. You might +also want to change the `initial_indexing_chunk_size` to be smaller if you're server is not very powerful, but not higher than `100_000`, +because meilisearch will refuse to process it if it's too big. However, in general you want this to be as big as possible, because meilisearch +indexes faster when it can process many posts in a single batch. + +> config :pleroma, Pleroma.Search.Meilisearch, +> url: "http://127.0.0.1:7700/", +> private_key: "private key", +> initial_indexing_chunk_size: 100_000 + +Information about setting up meilisearch can be found in the +[official documentation](https://docs.meilisearch.com/learn/getting_started/installation.html). +You probably want to start it with `MEILI_NO_ANALYTICS=true` environment variable to disable analytics. +At least version 0.25.0 is required, but you are strongly adviced to use at least 0.26.0, as it introduces +the `--enable-auto-batching` option which drastically improves performance. Without this option, the search +is hardly usable on a somewhat big instance. + +### Private key authentication (optional) + +To set the private key, use the `MEILI_MASTER_KEY` environment variable when starting. After setting the _master key_, +you have to get the _private key_, which is actually used for authentication. + +=== "OTP" + ```sh + ./bin/pleroma_ctl search.meilisearch show-keys <your master key here> + ``` + +=== "From Source" + ```sh + mix pleroma.search.meilisearch show-keys <your master key here> + ``` + +You will see a "Default Admin API Key", this is the key you actually put into your configuration file. + +### Initial indexing + +After setting up the configuration, you'll want to index all of your already existsing posts. Only public posts are indexed. You'll only +have to do it one time, but it might take a while, depending on the amount of posts your instance has seen. This is also a fairly RAM +consuming process for `meilisearch`, and it will take a lot of RAM when running if you have a lot of posts (seems to be around 5G for ~1.2 +million posts while idle and up to 7G while indexing initially, but your experience may be different). + +The sequence of actions is as follows: + +1. First, change the configuration to use `Pleroma.Search.Meilisearch` as the search backend +2. Restart your instance, at this point it can be used while the search indexing is running, though search won't return anything +3. Start the initial indexing process (as described below with `index`), + and wait until the task says it sent everything from the database to index +4. Wait until everything is actually indexed (by checking with `stats` as described below), + at this point you don't have to do anything, just wait a while. + +To start the initial indexing, run the `index` command: + +=== "OTP" + ```sh + ./bin/pleroma_ctl search.meilisearch index + ``` + +=== "From Source" + ```sh + mix pleroma.search.meilisearch index + ``` + +This will show you the total amount of posts to index, and then show you the amount of posts indexed currently, until the numbers eventually +become the same. The posts are indexed in big batches and meilisearch will take some time to actually index them, even after you have +inserted all the posts into it. Depending on the amount of posts, this may be as long as several hours. To get information about the status +of indexing and how many posts have actually been indexed, use the `stats` command: + +=== "OTP" + ```sh + ./bin/pleroma_ctl search.meilisearch stats + ``` + +=== "From Source" + ```sh + mix pleroma.search.meilisearch stats + ``` + +### Clearing the index + +In case you need to clear the index (for example, to re-index from scratch, if that needs to happen for some reason), you can +use the `clear` command: + +=== "OTP" + ```sh + ./bin/pleroma_ctl search.meilisearch clear + ``` + +=== "From Source" + ```sh + mix pleroma.search.meilisearch clear + ``` + +This will clear **all** the posts from the search index. Note, that deleted posts are also removed from index by the instance itself, so +there is no need to actually clear the whole index, unless you want **all** of it gone. That said, the index does not hold any information +that cannot be re-created from the database, it should also generally be a lot smaller than the size of your database. Still, the size +depends on the amount of text in posts. diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md index 50ed30d74..02513daf2 100644 --- a/docs/installation/freebsd_en.md +++ b/docs/installation/freebsd_en.md @@ -9,7 +9,7 @@ This document was written for FreeBSD 12.1, but should be work on future release This assumes the target system has `pkg(8)`. ``` -# pkg install elixir postgresql12-server postgresql12-client postgresql12-contrib git-lite sudo nginx gmake acme.sh cmake +# pkg install elixir postgresql12-server postgresql12-client postgresql12-contrib git-lite sudo nginx gmake acme.sh cmake vips ``` Copy the rc.d scripts to the right directory: @@ -41,6 +41,7 @@ Create a user for Pleroma: ``` # pw add user pleroma -m # echo 'export LC_ALL="en_US.UTF-8"' >> /home/pleroma/.profile +# echo 'export VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS' >> /home/pleroma/.profile # su -l pleroma ``` diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include index dcaacfdfd..3365a36a8 100644 --- a/docs/installation/generic_dependencies.include +++ b/docs/installation/generic_dependencies.include @@ -1,11 +1,11 @@ ## Required dependencies -* PostgreSQL 9.6+ -* Elixir 1.10+ -* Erlang OTP 22.2+ +* PostgreSQL >=9.6 +* Elixir >=1.11.0 <1.15 +* Erlang OTP >=22.2.0 <26 * git * file / libmagic -* gcc (clang might also work) +* gcc or clang * GNU make * CMake diff --git a/lib/mix/tasks/pleroma/search/meilisearch.ex b/lib/mix/tasks/pleroma/search/meilisearch.ex new file mode 100644 index 000000000..8379a0c25 --- /dev/null +++ b/lib/mix/tasks/pleroma/search/meilisearch.ex @@ -0,0 +1,145 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.Search.Meilisearch do + require Pleroma.Constants + + import Mix.Pleroma + import Ecto.Query + + import Pleroma.Search.Meilisearch, + only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete: 1] + + def run(["index"]) do + start_pleroma() + Pleroma.HTML.compile_scrubbers() + + meili_version = + ( + {:ok, result} = meili_get("/version") + + result["pkgVersion"] + ) + + # The ranking rule syntax was changed but nothing about that is mentioned in the changelog + if not Version.match?(meili_version, ">= 0.25.0") do + raise "Meilisearch <0.24.0 not supported" + end + + {:ok, _} = + meili_post( + "/indexes/objects/settings/ranking-rules", + [ + "published:desc", + "words", + "exactness", + "proximity", + "typo", + "attribute", + "sort" + ] + ) + + {:ok, _} = + meili_post( + "/indexes/objects/settings/searchable-attributes", + [ + "content" + ] + ) + + IO.puts("Created indices. Starting to insert posts.") + + chunk_size = Pleroma.Config.get([Pleroma.Search.Meilisearch, :initial_indexing_chunk_size]) + + Pleroma.Repo.transaction( + fn -> + query = + from(Pleroma.Object, + # Only index public and unlisted posts which are notes and have some text + where: + fragment("data->>'type' = 'Note'") and + (fragment("data->'to' \\? ?", ^Pleroma.Constants.as_public()) or + fragment("data->'cc' \\? ?", ^Pleroma.Constants.as_public())), + order_by: [desc: fragment("data->'published'")] + ) + + count = query |> Pleroma.Repo.aggregate(:count, :data) + IO.puts("Entries to index: #{count}") + + Pleroma.Repo.stream( + query, + timeout: :infinity + ) + |> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1) + |> Stream.filter(fn o -> not is_nil(o) end) + |> Stream.chunk_every(chunk_size) + |> Stream.transform(0, fn objects, acc -> + new_acc = acc + Enum.count(objects) + + # Reset to the beginning of the line and rewrite it + IO.write("\r") + IO.write("Indexed #{new_acc} entries") + + {[objects], new_acc} + end) + |> Stream.each(fn objects -> + result = + meili_put( + "/indexes/objects/documents", + objects + ) + + with {:ok, res} <- result do + if not Map.has_key?(res, "uid") do + IO.puts("\nFailed to index: #{inspect(result)}") + end + else + e -> IO.puts("\nFailed to index due to network error: #{inspect(e)}") + end + end) + |> Stream.run() + end, + timeout: :infinity + ) + + IO.write("\n") + end + + def run(["clear"]) do + start_pleroma() + + meili_delete("/indexes/objects/documents") + end + + def run(["show-keys", master_key]) do + start_pleroma() + + endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url]) + + {:ok, result} = + Pleroma.HTTP.get( + Path.join(endpoint, "/keys"), + [{"Authorization", "Bearer #{master_key}"}] + ) + + decoded = Jason.decode!(result.body) + + if decoded["results"] do + Enum.each(decoded["results"], fn %{"description" => desc, "key" => key} -> + IO.puts("#{desc}: #{key}") + end) + else + IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}") + end + end + + def run(["stats"]) do + start_pleroma() + + {:ok, result} = meili_get("/indexes/objects/stats") + IO.puts("Number of entries: #{result["numberOfDocuments"]}") + IO.puts("Indexing? #{result["isIndexing"]}") + end +end diff --git a/lib/phoenix/transports/web_socket/raw.ex b/lib/phoenix/transports/web_socket/raw.ex index 8cf9c32a2..cf4fda79f 100644 --- a/lib/phoenix/transports/web_socket/raw.ex +++ b/lib/phoenix/transports/web_socket/raw.ex @@ -26,7 +26,6 @@ defmodule Phoenix.Transports.WebSocket.Raw do conn |> fetch_query_params |> Transport.transport_log(opts[:transport_log]) - |> Transport.force_ssl(handler, endpoint, opts) |> Transport.check_origin(handler, endpoint, opts) case conn do diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 3556aaf9e..8a512dc57 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -368,7 +368,7 @@ defmodule Pleroma.Activity do ) end - defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search + defdelegate search(user, query, options \\ []), to: Pleroma.Search.DatabaseSearch def direct_conversation_id(activity, for_user) do alias Pleroma.Conversation.Participation diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index e68a3c57e..52cd6e9a9 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -54,7 +54,6 @@ defmodule Pleroma.Application do Config.DeprecationWarnings.warn() Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() Pleroma.ApplicationRequirements.verify!() - setup_instrumenters() load_custom_modules() Pleroma.Docs.JSON.compile() limiters_setup() @@ -91,6 +90,7 @@ defmodule Pleroma.Application do # Define workers and child supervisors to be supervised children = [ + Pleroma.PromEx, Pleroma.Repo, Config.TransferTask, Pleroma.Emoji, @@ -170,29 +170,6 @@ defmodule Pleroma.Application do end end - defp setup_instrumenters do - require Prometheus.Registry - - if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do - :ok = - :telemetry.attach( - "prometheus-ecto", - [:pleroma, :repo, :query], - &Pleroma.Repo.Instrumenter.handle_event/4, - %{} - ) - - Pleroma.Repo.Instrumenter.setup() - end - - Pleroma.Web.Endpoint.MetricsExporter.setup() - Pleroma.Web.Endpoint.PipelineInstrumenter.setup() - - # Note: disabled until prometheus-phx is integrated into prometheus-phoenix: - # Pleroma.Web.Endpoint.Instrumenter.setup() - PrometheusPhx.setup() - end - defp cachex_children do [ build_cachex("used_captcha", ttl_interval: seconds_valid_interval()), @@ -322,7 +299,11 @@ defmodule Pleroma.Application do def limiters_setup do config = Config.get(ConcurrentLimiter, []) - [Pleroma.Web.RichMedia.Helpers, Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy] + [ + Pleroma.Web.RichMedia.Helpers, + Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, + Pleroma.Search + ] |> Enum.each(fn module -> mod_config = Keyword.get(config, module, []) diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index 44b1c1705..477eac35b 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -168,8 +168,6 @@ defmodule Pleroma.ApplicationRequirements do check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), - check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"), - check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"), check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe") ] diff --git a/lib/pleroma/config/getting.ex b/lib/pleroma/config/getting.ex index f9b66bba6..ec93fd02a 100644 --- a/lib/pleroma/config/getting.ex +++ b/lib/pleroma/config/getting.ex @@ -5,4 +5,11 @@ defmodule Pleroma.Config.Getting do @callback get(any()) :: any() @callback get(any(), any()) :: any() + + def get(key), do: get(key, nil) + def get(key, default), do: impl().get(key, default) + + def impl do + Application.get_env(:pleroma, :config_impl, Pleroma.Config) + end end diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 44a984019..6fd05b0e0 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -55,8 +55,7 @@ defmodule Pleroma.Config.TransferTask do started_applications = Application.started_applications() - # TODO: some problem with prometheus after restart! - reject = [nil, :prometheus, :postgrex] + reject = [nil, :postgrex] reject = if restart_pleroma? do diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index 6508f1947..456a8fd54 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -17,6 +17,8 @@ defmodule Pleroma.Docs.Generator do # This shouldn't be needed as all modules are expected to have module_info/1, # but in test enviroments some transient modules `:elixir_compiler_XX` # are loaded for some reason (where XX is a random integer). + Code.ensure_loaded(module) + if function_exported?(module, :module_info, 1) do module.module_info(:attributes) |> Keyword.get_values(:behaviour) diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 24c845fcd..07dfea55b 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -8,11 +8,12 @@ defmodule Pleroma.Helpers.MediaHelper do """ alias Pleroma.HTTP + alias Vix.Vips.Operation require Logger def missing_dependencies do - Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc -> + Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc -> if Pleroma.Utils.command_available?(executable) do acc else @@ -22,54 +23,22 @@ defmodule Pleroma.Helpers.MediaHelper do end def image_resize(url, options) do - with executable when is_binary(executable) <- System.find_executable("convert"), - {:ok, args} <- prepare_image_resize_args(options), - {:ok, env} <- HTTP.get(url, [], pool: :media), - {:ok, fifo_path} <- mkfifo() do - args = List.flatten([fifo_path, args]) - run_fifo(fifo_path, env, executable, args) + with {:ok, env} <- HTTP.get(url, [], pool: :media), + {:ok, resized} <- + Operation.thumbnail_buffer(env.body, options.max_width, + height: options.max_height, + size: :VIPS_SIZE_DOWN + ) do + if options[:format] == "png" do + Operation.pngsave_buffer(resized, Q: options[:quality]) + else + Operation.jpegsave_buffer(resized, Q: options[:quality], interlace: true) + end else - nil -> {:error, {:convert, :command_not_found}} {:error, _} = error -> error end end - defp prepare_image_resize_args( - %{max_width: max_width, max_height: max_height, format: "png"} = options - ) do - quality = options[:quality] || 85 - resize = Enum.join([max_width, "x", max_height, ">"]) - - args = [ - "-resize", - resize, - "-quality", - to_string(quality), - "png:-" - ] - - {:ok, args} - end - - defp prepare_image_resize_args(%{max_width: max_width, max_height: max_height} = options) do - quality = options[:quality] || 85 - resize = Enum.join([max_width, "x", max_height, ">"]) - - args = [ - "-interlace", - "Plane", - "-resize", - resize, - "-quality", - to_string(quality), - "jpg:-" - ] - - {:ok, args} - end - - defp prepare_image_resize_args(_), do: {:error, :missing_options} - # Note: video thumbnail is intentionally not resized (always has original dimensions) def video_framegrab(url) do with executable when is_binary(executable) <- System.find_executable("ffmpeg"), diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index 9756c66dc..101e5dc88 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -97,13 +97,9 @@ defmodule Pleroma.Instances.Instance do def reachable?(url_or_host) when is_binary(url_or_host), do: true def set_reachable(url_or_host) when is_binary(url_or_host) do - with host <- host(url_or_host), - %Instance{} = existing_record <- Repo.get_by(Instance, %{host: host}) do - {:ok, _instance} = - existing_record - |> changeset(%{unreachable_since: nil}) - |> Repo.update() - end + %Instance{host: host(url_or_host)} + |> changeset(%{unreachable_since: nil}) + |> Repo.insert(on_conflict: {:replace, [:unreachable_since]}, conflict_target: :host) end def set_reachable(_), do: {:error, nil} diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index aa137d250..fa5baf1a4 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -328,6 +328,52 @@ defmodule Pleroma.Object do end end + def increase_quotes_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + safe_jsonb_set(?, '{quotesCount}', + (coalesce((?->>'quotesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def decrease_quotes_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + safe_jsonb_set(?, '{quotesCount}', + (greatest(0, (?->>'quotesCount')::int - 1))::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + def increase_vote_count(ap_id, name, actor) do with %Object{} = object <- Object.normalize(ap_id, fetch: false), "Question" <- object.data["type"] do diff --git a/lib/pleroma/prom_ex.ex b/lib/pleroma/prom_ex.ex new file mode 100644 index 000000000..6608708b7 --- /dev/null +++ b/lib/pleroma/prom_ex.ex @@ -0,0 +1,49 @@ +defmodule Pleroma.PromEx do + use PromEx, otp_app: :pleroma + + alias PromEx.Plugins + + @impl true + def plugins do + [ + # PromEx built in plugins + Plugins.Application, + Plugins.Beam, + {Plugins.Phoenix, router: Pleroma.Web.Router, endpoint: Pleroma.Web.Endpoint}, + Plugins.Ecto, + Plugins.Oban + # Plugins.PhoenixLiveView, + # Plugins.Absinthe, + # Plugins.Broadway, + + # Add your own PromEx metrics plugins + # Pleroma.Users.PromExPlugin + ] + end + + @impl true + def dashboard_assigns do + [ + datasource_id: Pleroma.Config.get([Pleroma.PromEx, :datasource]), + default_selected_interval: "30s" + ] + end + + @impl true + def dashboards do + [ + # PromEx built in Grafana dashboards + {:prom_ex, "application.json"}, + {:prom_ex, "beam.json"}, + {:prom_ex, "phoenix.json"}, + {:prom_ex, "ecto.json"}, + {:prom_ex, "oban.json"} + # {:prom_ex, "phoenix_live_view.json"}, + # {:prom_ex, "absinthe.json"}, + # {:prom_ex, "broadway.json"}, + + # Add your dashboard definitions here with the format: {:otp_app, "path_in_priv"} + # {:pleroma, "/grafana_dashboards/user_metrics.json"} + ] + end +end diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index 515b0c1ff..a50a59b3b 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -11,8 +11,6 @@ defmodule Pleroma.Repo do import Ecto.Query require Logger - defmodule Instrumenter, do: use(Prometheus.EctoInstrumenter) - @doc """ Dynamically loads the repository url from the DATABASE_URL environment variable. diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 0ed51ad07..63c6cb45b 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -6,7 +6,6 @@ defmodule Pleroma.ScheduledActivity do use Ecto.Schema alias Ecto.Multi - alias Pleroma.Config alias Pleroma.Repo alias Pleroma.ScheduledActivity alias Pleroma.User @@ -20,6 +19,8 @@ defmodule Pleroma.ScheduledActivity do @min_offset :timer.minutes(5) + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + schema "scheduled_activities" do belongs_to(:user, User, type: FlakeId.Ecto.CompatType) field(:scheduled_at, :naive_datetime) @@ -87,7 +88,7 @@ defmodule Pleroma.ScheduledActivity do |> where([sa], type(sa.scheduled_at, :date) == type(^scheduled_at, :date)) |> select([sa], count(sa.id)) |> Repo.one() - |> Kernel.>=(Config.get([ScheduledActivity, :daily_user_limit])) + |> Kernel.>=(@config_impl.get([ScheduledActivity, :daily_user_limit])) end def exceeds_total_user_limit?(user_id) do @@ -95,7 +96,7 @@ defmodule Pleroma.ScheduledActivity do |> where(user_id: ^user_id) |> select([sa], count(sa.id)) |> Repo.one() - |> Kernel.>=(Config.get([ScheduledActivity, :total_user_limit])) + |> Kernel.>=(@config_impl.get([ScheduledActivity, :total_user_limit])) end def far_enough?(scheduled_at) when is_binary(scheduled_at) do @@ -123,7 +124,7 @@ defmodule Pleroma.ScheduledActivity do def create(%User{} = user, attrs) do Multi.new() |> Multi.insert(:scheduled_activity, new(user, attrs)) - |> maybe_add_jobs(Config.get([ScheduledActivity, :enabled])) + |> maybe_add_jobs(@config_impl.get([ScheduledActivity, :enabled])) |> Repo.transaction() |> transaction_response end diff --git a/lib/pleroma/search.ex b/lib/pleroma/search.ex new file mode 100644 index 000000000..3b266e59b --- /dev/null +++ b/lib/pleroma/search.ex @@ -0,0 +1,17 @@ +defmodule Pleroma.Search do + alias Pleroma.Workers.SearchIndexingWorker + + def add_to_index(%Pleroma.Activity{id: activity_id}) do + SearchIndexingWorker.enqueue("add_to_index", %{"activity" => activity_id}) + end + + def remove_from_index(%Pleroma.Object{id: object_id}) do + SearchIndexingWorker.enqueue("remove_from_index", %{"object" => object_id}) + end + + def search(query, options) do + search_module = Pleroma.Config.get([Pleroma.Search, :module], Pleroma.Activity) + + search_module.search(options[:for_user], query, options) + end +end diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/search/database_search.ex index 0b9b24aa4..c6311e0c7 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/search/database_search.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Activity.Search do +defmodule Pleroma.Search.DatabaseSearch do alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Object.Fetcher alias Pleroma.Pagination alias Pleroma.User @@ -13,8 +14,11 @@ defmodule Pleroma.Activity.Search do import Ecto.Query + @behaviour Pleroma.Search.SearchBackend + + @impl true def search(user, search_query, options \\ []) do - index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin + index_type = if Config.get([:database, :rum_enabled]), do: :rum, else: :gin limit = Enum.min([Keyword.get(options, :limit), 40]) offset = Keyword.get(options, :offset, 0) author = Keyword.get(options, :author) @@ -45,6 +49,12 @@ defmodule Pleroma.Activity.Search do end end + @impl true + def add_to_index(_activity), do: :ok + + @impl true + def remove_from_index(_object), do: :ok + def maybe_restrict_author(query, %User{} = author) do Activity.Queries.by_author(query, author) end @@ -136,8 +146,8 @@ defmodule Pleroma.Activity.Search do ) end - defp maybe_restrict_local(q, user) do - limit = Pleroma.Config.get([:instance, :limit_to_local_content], :unauthenticated) + def maybe_restrict_local(q, user) do + limit = Config.get([:instance, :limit_to_local_content], :unauthenticated) case {limit, user} do {:all, _} -> restrict_local(q) @@ -149,7 +159,7 @@ defmodule Pleroma.Activity.Search do defp restrict_local(q), do: where(q, local: true) - defp maybe_fetch(activities, user, search_query) do + def maybe_fetch(activities, user, search_query) do with true <- Regex.match?(~r/https?:/, search_query), {:ok, object} <- Fetcher.fetch_object_from_id(search_query), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), diff --git a/lib/pleroma/search/meilisearch.ex b/lib/pleroma/search/meilisearch.ex new file mode 100644 index 000000000..2bff663e8 --- /dev/null +++ b/lib/pleroma/search/meilisearch.ex @@ -0,0 +1,181 @@ +defmodule Pleroma.Search.Meilisearch do + require Logger + require Pleroma.Constants + + alias Pleroma.Activity + alias Pleroma.Config.Getting, as: Config + + import Pleroma.Search.DatabaseSearch + import Ecto.Query + + @behaviour Pleroma.Search.SearchBackend + + defp meili_headers do + private_key = Config.get([Pleroma.Search.Meilisearch, :private_key]) + + [{"Content-Type", "application/json"}] ++ + if is_nil(private_key), do: [], else: [{"Authorization", "Bearer #{private_key}"}] + end + + def meili_get(path) do + endpoint = Config.get([Pleroma.Search.Meilisearch, :url]) + + result = + Pleroma.HTTP.get( + Path.join(endpoint, path), + meili_headers() + ) + + with {:ok, res} <- result do + {:ok, Jason.decode!(res.body)} + end + end + + def meili_post(path, params) do + endpoint = Config.get([Pleroma.Search.Meilisearch, :url]) + + result = + Pleroma.HTTP.post( + Path.join(endpoint, path), + Jason.encode!(params), + meili_headers() + ) + + with {:ok, res} <- result do + {:ok, Jason.decode!(res.body)} + end + end + + def meili_put(path, params) do + endpoint = Config.get([Pleroma.Search.Meilisearch, :url]) + + result = + Pleroma.HTTP.request( + :put, + Path.join(endpoint, path), + Jason.encode!(params), + meili_headers(), + [] + ) + + with {:ok, res} <- result do + {:ok, Jason.decode!(res.body)} + end + end + + def meili_delete(path) do + endpoint = Config.get([Pleroma.Search.Meilisearch, :url]) + + with {:ok, _} <- + Pleroma.HTTP.request( + :delete, + Path.join(endpoint, path), + "", + meili_headers(), + [] + ) do + :ok + else + _ -> {:error, "Could not remove from index"} + end + end + + @impl true + def search(user, query, options \\ []) do + limit = Enum.min([Keyword.get(options, :limit), 40]) + offset = Keyword.get(options, :offset, 0) + author = Keyword.get(options, :author) + + res = + meili_post( + "/indexes/objects/search", + %{q: query, offset: offset, limit: limit} + ) + + with {:ok, result} <- res do + hits = result["hits"] |> Enum.map(& &1["ap"]) + + try do + hits + |> Activity.create_by_object_ap_id() + |> Activity.with_preloaded_object() + |> Activity.restrict_deactivated_users() + |> maybe_restrict_local(user) + |> maybe_restrict_author(author) + |> maybe_restrict_blocked(user) + |> maybe_fetch(user, query) + |> order_by([object: obj], desc: obj.data["published"]) + |> Pleroma.Repo.all() + rescue + _ -> maybe_fetch([], user, query) + end + end + end + + def object_to_search_data(object) do + # Only index public or unlisted Notes + if not is_nil(object) and object.data["type"] == "Note" and + not is_nil(object.data["content"]) and + (Pleroma.Constants.as_public() in object.data["to"] or + Pleroma.Constants.as_public() in object.data["cc"]) and + object.data["content"] not in ["", "."] do + data = object.data + + content_str = + case data["content"] do + [nil | rest] -> to_string(rest) + str -> str + end + + content = + with {:ok, scrubbed} <- + FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing), + trimmed <- String.trim(scrubbed) do + trimmed + end + + # Make sure we have a non-empty string + if content != "" do + {:ok, published, _} = DateTime.from_iso8601(data["published"]) + + %{ + id: object.id, + content: content, + ap: data["id"], + published: published |> DateTime.to_unix() + } + end + end + end + + @impl true + def add_to_index(activity) do + maybe_search_data = object_to_search_data(activity.object) + + if activity.data["type"] == "Create" and maybe_search_data do + result = + meili_put( + "/indexes/objects/documents", + [maybe_search_data] + ) + + with {:ok, %{"status" => "enqueued"}} <- result do + # Added successfully + :ok + else + _ -> + # There was an error, report it + Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}") + {:error, result} + end + else + # The post isn't something we can search, that's ok + :ok + end + end + + @impl true + def remove_from_index(object) do + meili_delete("/indexes/objects/documents/#{object.id}") + end +end diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex new file mode 100644 index 000000000..a42e2f5f6 --- /dev/null +++ b/lib/pleroma/search/search_backend.ex @@ -0,0 +1,24 @@ +defmodule Pleroma.Search.SearchBackend do + @doc """ + Search statuses with a query, restricting to only those the user should have access to. + """ + @callback search(user :: Pleroma.User.t(), query :: String.t(), options :: [any()]) :: [ + Pleroma.Activity.t() + ] + + @doc """ + Add the object associated with the activity to the search index. + + The whole activity is passed, to allow filtering on things such as scope. + """ + @callback add_to_index(activity :: Pleroma.Activity.t()) :: :ok | {:error, any()} + + @doc """ + Remove the object from the index. + + Just the object, as opposed to the whole activity, is passed, since the object + is what contains the actual content and there is no need for fitlering when removing + from index. + """ + @callback remove_from_index(object :: Pleroma.Object.t()) :: {:ok, any()} | {:error, any()} +end diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 4aee9326f..bedd7889a 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -34,7 +34,6 @@ defmodule Pleroma.Upload do """ alias Ecto.UUID - alias Pleroma.Config alias Pleroma.Maps alias Pleroma.Web.ActivityPub.Utils require Logger @@ -76,6 +75,8 @@ defmodule Pleroma.Upload do :path ] + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + defp get_description(upload) do case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do {description, _} when is_binary(description) -> description @@ -244,18 +245,18 @@ defmodule Pleroma.Upload do defp url_from_spec(_upload, _base_url, {:url, url}), do: url def base_url do - uploader = Config.get([Pleroma.Upload, :uploader]) - upload_base_url = Config.get([Pleroma.Upload, :base_url]) - public_endpoint = Config.get([uploader, :public_endpoint]) + uploader = @config_impl.get([Pleroma.Upload, :uploader]) + upload_base_url = @config_impl.get([Pleroma.Upload, :base_url]) + public_endpoint = @config_impl.get([uploader, :public_endpoint]) case uploader do Pleroma.Uploaders.Local -> upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/" Pleroma.Uploaders.S3 -> - bucket = Config.get([Pleroma.Uploaders.S3, :bucket]) - truncated_namespace = Config.get([Pleroma.Uploaders.S3, :truncated_namespace]) - namespace = Config.get([Pleroma.Uploaders.S3, :bucket_namespace]) + bucket = @config_impl.get([Pleroma.Uploaders.S3, :bucket]) + truncated_namespace = @config_impl.get([Pleroma.Uploaders.S3, :truncated_namespace]) + namespace = @config_impl.get([Pleroma.Uploaders.S3, :bucket_namespace]) bucket_with_namespace = cond do diff --git a/lib/pleroma/upload/filter/analyze_metadata.ex b/lib/pleroma/upload/filter/analyze_metadata.ex index 9a76a998b..e510ae3e6 100644 --- a/lib/pleroma/upload/filter/analyze_metadata.ex +++ b/lib/pleroma/upload/filter/analyze_metadata.ex @@ -8,22 +8,23 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do """ require Logger + alias Vix.Vips.Image + alias Vix.Vips.Operation + @behaviour Pleroma.Upload.Filter @spec filter(Pleroma.Upload.t()) :: {:ok, :filtered, Pleroma.Upload.t()} | {:ok, :noop} | {:error, String.t()} def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do try do - image = - file - |> Mogrify.open() - |> Mogrify.verbose() + {:ok, image} = Image.new_from_file(file) + {width, height} = {Image.width(image), Image.height(image)} upload = upload - |> Map.put(:width, image.width) - |> Map.put(:height, image.height) - |> Map.put(:blurhash, get_blurhash(file)) + |> Map.put(:width, width) + |> Map.put(:height, height) + |> Map.put(:blurhash, get_blurhash(image)) {:ok, :filtered, upload} rescue @@ -53,7 +54,7 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do def filter(_), do: {:ok, :noop} defp get_blurhash(file) do - with {:ok, blurhash} <- :eblurhash.magick(file) do + with {:ok, blurhash} <- vips_blurhash(file) do blurhash else _ -> nil @@ -77,7 +78,28 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do %{width: width, height: height} else nil -> {:error, {:ffprobe, :command_not_found}} - {:error, _} = error -> error + error -> {:error, error} + end + end + + defp vips_blurhash(%Vix.Vips.Image{} = image) do + with {:ok, resized_image} <- Operation.thumbnail_image(image, 100), + {height, width} <- {Image.height(resized_image), Image.width(resized_image)}, + max <- max(height, width), + {x, y} <- {max(round(width * 5 / max), 1), max(round(height * 5 / max), 1)} do + {:ok, rgb} = + if Image.has_alpha?(resized_image) do + # remove alpha channel + resized_image + |> Operation.extract_band!(0, n: 3) + |> Image.write_to_binary() + else + Image.write_to_binary(resized_image) + end + + Blurhash.encode(rgb, width, height, x, y) + else + _ -> nil end end end diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex index 543b22031..8c1ed82f8 100644 --- a/lib/pleroma/upload/filter/exiftool/read_description.ex +++ b/lib/pleroma/upload/filter/exiftool/read_description.ex @@ -10,8 +10,6 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do """ @behaviour Pleroma.Upload.Filter - @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - def filter(%Pleroma.Upload{description: description}) when is_binary(description), do: {:ok, :noop} diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index 19287c532..7b32bd8a5 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -6,7 +6,8 @@ defmodule Pleroma.Uploaders.S3 do @behaviour Pleroma.Uploaders.Uploader require Logger - alias Pleroma.Config + @ex_aws_impl Application.compile_env(:pleroma, [__MODULE__, :ex_aws_impl], ExAws) + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) # The file name is re-encoded with S3's constraints here to comply with previous # links with less strict filenames @@ -22,7 +23,7 @@ defmodule Pleroma.Uploaders.S3 do @impl true def put_file(%Pleroma.Upload{} = upload) do - config = Config.get([__MODULE__]) + config = @config_impl.get([__MODULE__]) bucket = Keyword.get(config, :bucket) streaming = Keyword.get(config, :streaming_enabled) @@ -56,7 +57,7 @@ defmodule Pleroma.Uploaders.S3 do ]) end - case ExAws.request(op) do + case @ex_aws_impl.request(op) do {:ok, _} -> {:ok, {:file, s3_name}} @@ -69,9 +70,9 @@ defmodule Pleroma.Uploaders.S3 do @impl true def delete_file(file) do [__MODULE__, :bucket] - |> Config.get() + |> @config_impl.get() |> ExAws.S3.delete_object(file) - |> ExAws.request() + |> @ex_aws_impl.request() |> case do {:ok, %{status_code: 204}} -> :ok error -> {:error, inspect(error)} @@ -83,3 +84,7 @@ defmodule Pleroma.Uploaders.S3 do String.replace(name, @regex, "-") end end + +defmodule Pleroma.Uploaders.S3.ExAwsAPI do + @callback request(op :: ExAws.Operation.t()) :: {:ok, ExAws.Operation.t()} | {:error, term()} +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ce125d608..0706f5607 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2136,7 +2136,7 @@ defmodule Pleroma.User do def public_key(_), do: {:error, "key not found"} def get_public_key_for_ap_id(ap_id) do - with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), + with %User{} = user <- get_cached_by_ap_id(ap_id), {:ok, public_key} <- public_key(user) do {:ok, public_key} else diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index 447fca2a1..04dc8e0ce 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -35,6 +35,8 @@ defmodule Pleroma.User.Backup do timestamps() end + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + def create(user, admin_id \\ nil) do with :ok <- validate_limit(user, admin_id), {:ok, backup} <- user |> new() |> Repo.insert() do @@ -124,7 +126,10 @@ defmodule Pleroma.User.Backup do |> Repo.update() end - def process(%__MODULE__{} = backup) do + def process( + %__MODULE__{} = backup, + processor_module \\ __MODULE__.Processor + ) do set_state(backup, :running, 0) current_pid = self() @@ -132,7 +137,7 @@ defmodule Pleroma.User.Backup do task = Task.Supervisor.async_nolink( Pleroma.TaskSupervisor, - __MODULE__, + processor_module, :do_process, [backup, current_pid] ) @@ -140,25 +145,8 @@ defmodule Pleroma.User.Backup do wait_backup(backup, backup.processed_number, task) end - def do_process(backup, current_pid) do - with {:ok, zip_file} <- export(backup, current_pid), - {:ok, %{size: size}} <- File.stat(zip_file), - {:ok, _upload} <- upload(backup, zip_file) do - backup - |> cast( - %{ - file_size: size, - processed: true, - state: :complete - }, - [:file_size, :processed, :state] - ) - |> Repo.update() - end - end - defp wait_backup(backup, current_processed, task) do - wait_time = Pleroma.Config.get([__MODULE__, :process_wait_time]) + wait_time = @config_impl.get([__MODULE__, :process_wait_time]) receive do {:progress, new_processed} -> @@ -365,3 +353,35 @@ defmodule Pleroma.User.Backup do ) end end + +defmodule Pleroma.User.Backup.ProcessorAPI do + @callback do_process(%Pleroma.User.Backup{}, pid()) :: + {:ok, %Pleroma.User.Backup{}} | {:error, any()} +end + +defmodule Pleroma.User.Backup.Processor do + @behaviour Pleroma.User.Backup.ProcessorAPI + + alias Pleroma.Repo + alias Pleroma.User.Backup + + import Ecto.Changeset + + @impl true + def do_process(backup, current_pid) do + with {:ok, zip_file} <- Backup.export(backup, current_pid), + {:ok, %{size: size}} <- File.stat(zip_file), + {:ok, _upload} <- Backup.upload(backup, zip_file) do + backup + |> cast( + %{ + file_size: size, + processed: true, + state: :complete + }, + [:file_size, :processed, :state] + ) + |> Repo.update() + end + end +end diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex index aee41b0fe..7a8b176cd 100644 --- a/lib/pleroma/web.ex +++ b/lib/pleroma/web.ex @@ -136,7 +136,7 @@ defmodule Pleroma.Web do namespace: Pleroma.Web # Import convenience functions from controllers - import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1] + import Phoenix.Controller, only: [get_csrf_token: 0, view_module: 1] import Pleroma.Web.ErrorHelpers import Pleroma.Web.Gettext diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 3979d418e..32d1a1037 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -96,6 +96,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp increase_replies_count_if_reply(_create_data), do: :noop + defp increase_quotes_count_if_quote(%{ + "object" => %{"quoteUrl" => quote_ap_id} = object, + "type" => "Create" + }) do + if is_public?(object) do + Object.increase_quotes_count(quote_ap_id) + end + end + + defp increase_quotes_count_if_quote(_create_data), do: :noop + @object_types ~w[ChatMessage Question Answer Audio Video Image Event Article Note Page] @impl true def persist(%{"type" => type} = object, meta) when type in @object_types do @@ -140,6 +151,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) end) + # Add local posts to search index + if local, do: Pleroma.Search.add_to_index(activity) + {:ok, activity} else %Activity{} = activity -> @@ -299,6 +313,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with {:ok, activity} <- insert(create_data, local, fake), {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), + _ <- increase_quotes_count_if_quote(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), {:ok, _actor} <- update_last_status_at_if_public(actor, activity), @@ -1237,6 +1252,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_unauthenticated(query, _), do: query + defp restrict_quote_url(query, %{quote_url: quote_url}) do + from([_activity, object] in query, + where: fragment("(?)->'quoteUrl' = ?", object.data, ^quote_url) + ) + end + + defp restrict_quote_url(query, _), do: query + defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query defp exclude_poll_votes(query, _) do @@ -1399,6 +1422,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_instance(opts) |> restrict_announce_object_actor(opts) |> restrict_filtered(opts) + |> restrict_quote_url(opts) |> maybe_restrict_deactivated_users(opts) |> exclude_poll_votes(opts) |> exclude_chat_messages(opts) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 1357c379c..3b2193ca3 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -287,10 +287,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end - def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do - conn - |> put_status(:bad_request) - |> json("Invalid HTTP Signature") + def inbox(%{assigns: %{valid_signature: false}, req_headers: req_headers} = conn, params) do + Federator.incoming_ap_doc(%{req_headers: req_headers, params: params}) + json(conn, "ok") end # POST /relay/inbox -or- POST /internal/fetch/inbox diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index ff9f84497..69054e25b 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -54,6 +54,8 @@ defmodule Pleroma.Web.ActivityPub.MRF do @required_description_keys [:key, :related_policy] def filter_one(policy, message) do + Code.ensure_loaded(policy) + should_plug_history? = if function_exported?(policy, :history_awareness, 0) do policy.history_awareness() @@ -188,6 +190,8 @@ defmodule Pleroma.Web.ActivityPub.MRF do def config_descriptions(policies) do Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc -> + Code.ensure_loaded(policy) + if function_exported?(policy, :config_description, 0) do description = @default_description diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 835ed97b7..1a5d02601 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) + field(:quotes_count, :integer, default: 0) field(:inReplyTo, ObjectValidators.ObjectID) field(:quoteUrl, ObjectValidators.ObjectID) field(:url, ObjectValidators.BareUri) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 098c177c7..10f268f05 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -197,6 +197,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Increase replies count # - Set up ActivityExpiration # - Set up notifications + # - Index incoming posts for search (if needed) @impl true def handle(%{data: %{"type" => "Create"}} = activity, meta) do with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta), @@ -209,6 +210,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do Object.increase_replies_count(in_reply_to) end + if quote_url = object.data["quoteUrl"] do + Object.increase_quotes_count(quote_url) + end + reply_depth = (meta[:depth] || 0) + 1 # FIXME: Force inReplyTo to replies @@ -226,6 +231,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) end) + Pleroma.Search.add_to_index(Map.put(activity, :object, object)) + meta = meta |> add_notifications(notifications) @@ -285,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Reduce the user note count # - Reduce the reply count # - Stream out the activity + # - Removes posts from search index (if needed) @impl true def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do deleted_object = @@ -305,6 +313,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do Object.decrease_replies_count(in_reply_to) end + if quote_url = deleted_object.data["quoteUrl"] do + Object.decrease_quotes_count(quote_url) + end + MessageReference.delete_for_object(deleted_object) ap_streamer().stream_out(object) @@ -323,6 +335,11 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end if result == :ok do + # Only remove from index when deleting actual objects, not users or anything else + with %Pleroma.Object{} <- deleted_object do + Pleroma.Search.remove_from_index(deleted_object) + end + {:ok, object, meta} else {:error, result} diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 437220077..b32f19740 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Ecto.UUID alias Pleroma.Activity alias Pleroma.Config + alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID alias Pleroma.Maps alias Pleroma.Notification alias Pleroma.Object @@ -852,9 +853,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do [actor | reported_activities] = activity.data["object"] stripped_activities = - Enum.map(reported_activities, fn - act when is_map(act) -> act["id"] - act when is_binary(act) -> act + Enum.reduce(reported_activities, [], fn act, acc -> + case ObjectID.cast(act) do + {:ok, act} -> [act | acc] + _ -> acc + end end) new_data = put_in(activity.data, ["object"], [actor | stripped_activities]) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex index ca40da930..141b60533 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex @@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do album: %Schema{type: :string, description: "The album of the media playing"}, artist: %Schema{type: :string, description: "The artist of the media playing"}, length: %Schema{type: :integer, description: "The length of the media playing"}, + externalLink: %Schema{type: :string, description: "A URL referencing the media playing"}, visibility: %Schema{ allOf: [VisibilityScope], default: "public", @@ -69,7 +70,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do "title" => "Some Title", "artist" => "Some Artist", "album" => "Some Album", - "length" => 180_000 + "length" => 180_000, + "externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title" } } end @@ -83,6 +85,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do title: %Schema{type: :string, description: "The title of the media playing"}, album: %Schema{type: :string, description: "The album of the media playing"}, artist: %Schema{type: :string, description: "The artist of the media playing"}, + externalLink: %Schema{type: :string, description: "A URL referencing the media playing"}, length: %Schema{ type: :integer, description: "The length of the media playing", @@ -97,6 +100,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do "artist" => "Some Artist", "album" => "Some Album", "length" => 180_000, + "externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title", "created_at" => "2019-09-28T12:40:45.000Z" } } diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex new file mode 100644 index 000000000..6e69c5269 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.StatusOperation + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def quotes_operation do + %Operation{ + tags: ["Retrieve status information"], + summary: "Quoted by", + description: "View quotes for a given status", + operationId: "PleromaAPI.StatusController.quotes", + parameters: [id_param() | pagination_params()], + security: [%{"oAuth" => ["read:statuses"]}], + responses: %{ + 200 => + Operation.response( + "Array of Status", + "application/json", + StatusOperation.array_of_statuses() + ), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def id_param do + Operation.parameter(:id, :path, FlakeID, "Status ID", + example: "9umDrYheeY451cQnEe", + required: true + ) + end +end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 07f03134a..a4052803b 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -213,6 +213,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :boolean, description: "`true` if the quoted post is visible to the user" }, + quotes_count: %Schema{ + type: :integer, + description: "How many statuses quoted this status" + }, local: %Schema{ type: :boolean, description: "`true` if the post was made on the local instance" @@ -367,7 +371,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "in_reply_to_account_acct" => nil, "local" => true, "spoiler_text" => %{"text/plain" => ""}, - "thread_muted" => false + "thread_muted" => false, + "quotes_count" => 0 }, "poll" => nil, "reblog" => nil, diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index ca1329284..8910ad5b8 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -83,7 +83,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp listen_object(draft) do object = draft.params - |> Map.take([:album, :artist, :title, :length]) + |> Map.take([:album, :artist, :title, :length, :externalLink]) |> Map.new(fn {key, value} -> {to_string(key), value} end) |> Map.put("type", "Audio") |> Map.put("to", draft.to) diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 574f3ab63..307fa069e 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -9,7 +9,20 @@ defmodule Pleroma.Web.Endpoint do alias Pleroma.Config - socket("/socket", Pleroma.Web.UserSocket) + socket("/socket", Pleroma.Web.UserSocket, + websocket: [ + path: "/websocket", + serializer: [ + {Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"}, + {Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"} + ], + timeout: 60_000, + transport_log: false, + compress: false + ], + longpoll: false + ) + socket("/live", Phoenix.LiveView.Socket) plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) @@ -138,47 +151,6 @@ defmodule Pleroma.Web.Endpoint do plug(Pleroma.Web.Plugs.RemoteIp) - defmodule Instrumenter do - use Prometheus.PhoenixInstrumenter - end - - defmodule PipelineInstrumenter do - use Prometheus.PlugPipelineInstrumenter - end - - defmodule MetricsExporter do - use Prometheus.PlugExporter - end - - defmodule MetricsExporterCaller do - @behaviour Plug - - def init(opts), do: opts - - def call(conn, opts) do - prometheus_config = Application.get_env(:prometheus, MetricsExporter, []) - ip_whitelist = List.wrap(prometheus_config[:ip_whitelist]) - - cond do - !prometheus_config[:enabled] -> - conn - - ip_whitelist != [] and - !Enum.find(ip_whitelist, fn ip -> - Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip} - end) -> - conn - - true -> - MetricsExporter.call(conn, opts) - end - end - end - - plug(PipelineInstrumenter) - - plug(MetricsExporterCaller) - plug(Pleroma.Web.Router) @doc """ diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 1a86f7a53..4a0885fab 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -17,10 +17,28 @@ defmodule Pleroma.Web.Fallback.RedirectController do |> json(%{error: "Not implemented"}) end + def add_generated_metadata(page_content, extra \\ "") do + title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>" + favicon = "<link rel='icon' href='#{Pleroma.Config.get([:instance, :favicon])}'>" + manifest = "<link rel='manifest' href='/manifest.json'>" + + page_content + |> String.replace( + "<!--server-generated-meta-->", + title <> favicon <> manifest <> extra + ) + end + def redirector(conn, _params, code \\ 200) do + {:ok, index_content} = File.read(index_file_path()) + + response = + index_content + |> add_generated_metadata() + conn |> put_resp_content_type("text/html") - |> send_file(code, index_file_path()) + |> send_resp(code, response) end def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do @@ -34,14 +52,12 @@ defmodule Pleroma.Web.Fallback.RedirectController do def redirector_with_meta(conn, params) do {:ok, index_content} = File.read(index_file_path()) - tags = build_tags(conn, params) preloads = preload_data(conn, params) - title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>" response = index_content - |> String.replace("<!--server-generated-meta-->", tags <> preloads <> title) + |> add_generated_metadata(tags <> preloads) conn |> put_resp_content_type("text/html") @@ -55,11 +71,10 @@ defmodule Pleroma.Web.Fallback.RedirectController do def redirector_with_preload(conn, params) do {:ok, index_content} = File.read(index_file_path()) preloads = preload_data(conn, params) - title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>" response = index_content - |> String.replace("<!--server-generated-meta-->", preloads <> title) + |> add_generated_metadata(preloads) conn |> put_resp_content_type("text/html") diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 84b77cda1..8621d984c 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -35,6 +35,17 @@ defmodule Pleroma.Web.Federator do end # Client API + def incoming_ap_doc(%{params: params, req_headers: req_headers}) do + ReceiverWorker.enqueue( + "incoming_ap_doc", + %{"req_headers" => req_headers, "params" => params, "timeout" => :timer.seconds(20)}, + priority: 2 + ) + end + + def incoming_ap_doc(%{"type" => "Delete"} = params) do + ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}, priority: 3) + end def incoming_ap_doc(params) do ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}) diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 5e6e04734..e4acba226 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller - alias Pleroma.Activity alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ControllerHelper @@ -100,7 +99,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do end defp resource_search(_, "statuses", query, options) do - statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end) + statuses = with_fallback(fn -> Pleroma.Search.search(query, options) end) StatusView.render("index.json", activities: statuses, diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index cc3e3582f..237de3055 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountView do @@ -249,6 +249,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do nil end + last_status_at = + user.last_status_at && + user.last_status_at |> NaiveDateTime.to_date() |> Date.to_iso8601() + %{ id: to_string(user.id), username: username_from_nickname(user.nickname), @@ -277,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do actor_type: user.actor_type } }, - last_status_at: user.last_status_at, + last_status_at: last_status_at, # Pleroma extensions # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 8825f3715..0e2e604f5 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -447,7 +447,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do thread_muted: thread_muted?, emoji_reactions: emoji_reactions, parent_visible: visible_for_user?(reply_to, opts[:for]), - pinned_at: pinned_at + pinned_at: pinned_at, + quotes_count: object.data["quotesCount"] || 0 } } end diff --git a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex new file mode 100644 index 000000000..482662fdd --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex @@ -0,0 +1,66 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.StatusController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] + + require Ecto.Query + require Pleroma.Constants + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :quotes + ) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaStatusOperation + + @doc "GET /api/v1/pleroma/statuses/:id/quotes" + def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do + with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do + params = + params + |> Map.put(:type, "Create") + |> Map.put(:blocking_user, user) + |> Map.put(:quote_url, object.data["id"]) + + recipients = + if user do + [Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)] + else + [Pleroma.Constants.as_public()] + end + + activities = + recipients + |> ActivityPub.fetch_activities(params) + |> Enum.reverse() + + conn + |> add_link_headers(activities) + |> put_view(StatusView) + |> render("index.json", + activities: activities, + for: user, + as: :activity + ) + else + nil -> {:error, :not_found} + false -> {:error, :not_found} + end + end +end diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex index a5985fb2a..edf0a2390 100644 --- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex +++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -27,6 +27,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do title: object.data["title"] |> HTML.strip_tags(), artist: object.data["artist"] |> HTML.strip_tags(), album: object.data["album"] |> HTML.strip_tags(), + externalLink: object.data["externalLink"], length: object.data["length"] } end diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 0488df30e..61000bb9b 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -4,11 +4,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.HTML alias Pleroma.Object alias Pleroma.Web.RichMedia.Parser + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + @options [ pool: :media, max_body: 2_000_000, @@ -17,7 +18,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do @spec validate_page_url(URI.t() | binary()) :: :ok | :error defp validate_page_url(page_url) when is_binary(page_url) do - validate_tld = Config.get([Pleroma.Formatter, :validate_tld]) + validate_tld = @config_impl.get([Pleroma.Formatter, :validate_tld]) page_url |> Linkify.Parser.url?(validate_tld: validate_tld) @@ -27,10 +28,10 @@ defmodule Pleroma.Web.RichMedia.Helpers do defp validate_page_url(%URI{host: host, scheme: "https", authority: authority}) when is_binary(authority) do cond do - host in Config.get([:rich_media, :ignore_hosts], []) -> + host in @config_impl.get([:rich_media, :ignore_hosts], []) -> :error - get_tld(host) in Config.get([:rich_media, :ignore_tld], []) -> + get_tld(host) in @config_impl.get([:rich_media, :ignore_tld], []) -> :error true -> @@ -56,7 +57,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do end def fetch_data_for_object(object) do - with true <- Config.get([:rich_media, :enabled]), + with true <- @config_impl.get([:rich_media, :enabled]), {:ok, page_url} <- HTML.extract_first_external_url_from_object(object), :ok <- validate_page_url(page_url), @@ -68,7 +69,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do end def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do - with true <- Config.get([:rich_media, :enabled]), + with true <- @config_impl.get([:rich_media, :enabled]), %Object{} = object <- Object.normalize(activity, fetch: false) do fetch_data_for_object(object) else diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6b9e158a3..38391cc8f 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -224,6 +224,12 @@ defmodule Pleroma.Web.Router do post("/remote_interaction", UtilController, :remote_interaction) end + scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do + pipe_through(:pleroma_api) + + get("/federation_status", InstancesController, :show) + end + scope "/api/v1/pleroma", Pleroma.Web do pipe_through(:pleroma_api) post("/uploader_callback/:upload_path", UploaderController, :callback) @@ -465,6 +471,8 @@ defmodule Pleroma.Web.Router do get("/main/ostatus", UtilController, :show_subscribe_form) get("/ostatus_subscribe", RemoteFollowController, :follow) post("/ostatus_subscribe", RemoteFollowController, :do_follow) + + get("/authorize_interaction", RemoteFollowController, :authorize_interaction) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do @@ -578,6 +586,8 @@ defmodule Pleroma.Web.Router do pipe_through(:api) get("/accounts/:id/favourites", AccountController, :favourites) get("/accounts/:id/endorsements", AccountController, :endorsements) + + get("/statuses/:id/quotes", StatusController, :quotes) end scope [] do @@ -602,7 +612,6 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) get("/accounts/:id/scrobbles", ScrobbleController, :index) - get("/federation_status", InstancesController, :show) end scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do @@ -1003,9 +1012,8 @@ defmodule Pleroma.Web.Router do options("/*path", RedirectController, :empty) end - # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+ def get_api_routes do - __MODULE__.__routes__() + Phoenix.Router.routes(__MODULE__) |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end) |> Enum.map(fn r -> r.path diff --git a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex index e45d13bdf..e3639aae7 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex @@ -1,8 +1,8 @@ -<%= if get_flash(@conn, :info) do %> -<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> +<%= if Phoenix.Flash.get(@flash, :info) do %> +<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p> <% end %> -<%= if get_flash(@conn, :error) do %> -<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> +<%= if Phoenix.Flash.get(@flash, :error) do %> +<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p> <% end %> <h2><%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %></h2> diff --git a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex index 50e6c04b6..f995b8805 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex @@ -1,8 +1,8 @@ -<%= if get_flash(@conn, :info) do %> -<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> +<%= if Phoenix.Flash.get(@flash, :info) do %> +<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p> <% end %> -<%= if get_flash(@conn, :error) do %> -<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> +<%= if Phoenix.Flash.get(@flash, :error) do %> +<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p> <% end %> <h2><%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %></h2> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex index 1f661efb2..e7f65266f 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex @@ -1,8 +1,8 @@ -<%= if get_flash(@conn, :info) do %> - <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> +<%= if Phoenix.Flash.get(@flash, :info) do %> + <p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p> <% end %> -<%= if get_flash(@conn, :error) do %> - <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> +<%= if Phoenix.Flash.get(@flash, :error) do %> + <p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p> <% end %> <h2><%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %></h2> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index b3654f3eb..5b38f7142 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -1,8 +1,8 @@ -<%= if get_flash(@conn, :info) do %> -<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> +<%= if Phoenix.Flash.get(@flash, :info) do %> +<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p> <% end %> -<%= if get_flash(@conn, :error) do %> -<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> +<%= if Phoenix.Flash.get(@flash, :error) do %> +<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p> <% end %> <%= form_for @conn, Routes.o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %> diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index 6229d5d05..178ad2b43 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -121,6 +121,13 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."}) end + # GET /authorize_interaction + # + def authorize_interaction(conn, %{"uri" => uri}) do + conn + |> redirect(to: Routes.remote_follow_path(conn, :follow, %{acct: uri})) + end + defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee}) end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index d5a24ae6c..ca8a98960 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -345,13 +345,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end def healthcheck(conn, _params) do - with true <- Config.get([:instance, :healthcheck]), + with {:cfg, true} <- {:cfg, Config.get([:instance, :healthcheck])}, %{healthy: true} = info <- Healthcheck.system_info() do json(conn, info) else %{healthy: false} = info -> service_unavailable(conn, info) + {:cfg, false} -> + service_unavailable(conn, %{"error" => "Healthcheck disabled"}) + _ -> service_unavailable(conn, %{}) end diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index 1540c1605..0292bbb3b 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do The worker to send digest emails. """ - use Oban.Worker, queue: "digest_emails" + use Oban.Worker, queue: "mailer" alias Pleroma.Config alias Pleroma.Emails diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index 267fe2837..1c3e445aa 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do import Ecto.Query - use Pleroma.Workers.WorkerHelper, queue: "new_users_digest" + use Pleroma.Workers.WorkerHelper, queue: "mailer" @impl Oban.Worker def perform(_job) do diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index cf1bb62b4..1dddd8d2e 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -3,24 +3,56 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ReceiverWorker do + alias Pleroma.Signature + alias Pleroma.User alias Pleroma.Web.Federator use Pleroma.Workers.WorkerHelper, queue: "federator_incoming" @impl Oban.Worker + + def perform(%Job{ + args: %{"op" => "incoming_ap_doc", "req_headers" => req_headers, "params" => params} + }) do + # Oban's serialization converts our tuple headers to lists. + # Revert it for the signature validation. + req_headers = Enum.into(req_headers, [], &List.to_tuple(&1)) + + conn_data = %{params: params, req_headers: req_headers} + + with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]), + {:ok, _public_key} <- Signature.refetch_public_key(conn_data), + {:signature, true} <- {:signature, HTTPSignatures.validate_conn(conn_data)}, + {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do + {:ok, res} + else + e -> process_errors(e) + end + end + def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do {:ok, res} else + e -> process_errors(e) + end + end + + @impl Oban.Worker + def timeout(%_{args: %{"timeout" => timeout}}), do: timeout + + def timeout(_job), do: :timer.seconds(5) + + defp process_errors(errors) do + case errors do {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} {:error, :already_present} -> {:cancel, :already_present} {:error, {:validate_object, reason}} -> {:cancel, reason} {:error, {:error, {:validate, reason}}} -> {:cancel, reason} {:error, {:reject, reason}} -> {:cancel, reason} + {:signature, false} -> {:cancel, :invalid_signature} + {:error, {:error, reason = "Object has been deleted"}} -> {:cancel, reason} e -> e end end - - @impl Oban.Worker - def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/search_indexing_worker.ex b/lib/pleroma/workers/search_indexing_worker.ex new file mode 100644 index 000000000..8476a2be5 --- /dev/null +++ b/lib/pleroma/workers/search_indexing_worker.ex @@ -0,0 +1,23 @@ +defmodule Pleroma.Workers.SearchIndexingWorker do + use Pleroma.Workers.WorkerHelper, queue: "search_indexing" + + @impl Oban.Worker + + alias Pleroma.Config.Getting, as: Config + + def perform(%Job{args: %{"op" => "add_to_index", "activity" => activity_id}}) do + activity = Pleroma.Activity.get_by_id_with_object(activity_id) + + search_module = Config.get([Pleroma.Search, :module]) + + search_module.add_to_index(activity) + end + + def perform(%Job{args: %{"op" => "remove_from_index", "object" => object_id}}) do + object = Pleroma.Object.get_by_id(object_id) + + search_module = Config.get([Pleroma.Search, :module]) + + search_module.remove_from_index(object) + end +end @@ -7,7 +7,7 @@ defmodule Pleroma.Mixfile do version: version("2.6.50"), elixir: "~> 1.11", elixirc_paths: elixirc_paths(Mix.env()), - compilers: [:phoenix] ++ Mix.compilers(), + compilers: Mix.compilers(), elixirc_options: [warnings_as_errors: warnings_as_errors()], xref: [exclude: [:eldap]], start_permanent: Mix.env() == :prod, @@ -113,18 +113,19 @@ defmodule Pleroma.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.6.0"}, - {:phoenix_ecto, "~> 4.4.0"}, + {:phoenix, "~> 1.7.3"}, + {:phoenix_ecto, "~> 4.4"}, + {:ecto_sql, "~> 3.10"}, {:ecto_enum, "~> 1.4"}, {:postgrex, ">= 0.0.0"}, - {:phoenix_html, "~> 3.1"}, + {:phoenix_html, "~> 3.3"}, {:phoenix_live_reload, "~> 1.3.3", only: :dev}, - {:phoenix_live_view, "~> 0.17.1"}, - {:phoenix_live_dashboard, "~> 0.6.2"}, - {:telemetry_metrics, "~> 0.6.1"}, + {:phoenix_live_view, "~> 0.19.0"}, + {:phoenix_live_dashboard, "~> 0.8.0"}, + {:telemetry_metrics, "~> 0.6"}, {:telemetry_poller, "~> 1.0"}, {:tzdata, "~> 1.0.3"}, - {:plug_cowboy, "~> 2.3"}, + {:plug_cowboy, "~> 2.5"}, # oban 2.14 requires Elixir 1.12+ {:oban, "~> 2.13.4"}, {:gettext, "~> 0.20"}, @@ -139,9 +140,9 @@ defmodule Pleroma.Mixfile do {:castore, "~> 0.1"}, {:cowlib, "~> 2.9", override: true}, {:gun, "~> 2.0.0-rc.1", override: true}, - {:finch, "~> 0.10.0"}, + {:finch, "~> 0.15"}, {:jason, "~> 1.2"}, - {:mogrify, "~> 0.9.1"}, + {:mogrify, "~> 0.8.0"}, {:ex_aws, "~> 2.1.6"}, {:ex_aws_s3, "~> 2.0"}, {:sweet_xml, "~> 0.7.2"}, @@ -162,21 +163,9 @@ defmodule Pleroma.Mixfile do {:http_signatures, "~> 0.1.1"}, {:telemetry, "~> 1.0.0", override: true}, {:poolboy, "~> 1.5"}, - {:prometheus, "~> 4.6"}, - {:prometheus_ex, - git: "https://github.com/lanodan/prometheus.ex.git", - branch: "fix/elixir-1.14", - override: true}, - {:prometheus_plugs, "~> 1.1"}, - {:prometheus_phoenix, "~> 1.3"}, - # Note: once `prometheus_phx` is integrated into `prometheus_phoenix`, remove the former: - {:prometheus_phx, - git: "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", - branch: "no-logging"}, - {:prometheus_ecto, "~> 1.4"}, + {:prom_ex, "~> 1.9"}, {:recon, "~> 2.5"}, {:joken, "~> 2.0"}, - {:benchee, "~> 1.0"}, {:pot, "~> 1.0"}, {:ex_const, "~> 0.2"}, {:plug_static_index_html, "~> 1.0.0"}, @@ -187,12 +176,14 @@ defmodule Pleroma.Mixfile do ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"}, {:captcha, git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", - ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, + ref: "90f6ce7672f70f56708792a98d98bd05176c9176"}, {:restarter, path: "./restarter"}, {:majic, "~> 1.0"}, - {:eblurhash, "~> 1.2.2"}, {:open_api_spex, "~> 3.16"}, {:ecto_psql_extras, "~> 0.6"}, + {:vix, "~> 0.26.0"}, + {:elixir_make, "~> 0.7.7", override: true}, + {:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash}, ## dev & test {:ex_doc, "~> 0.22", only: :dev, runtime: false}, @@ -202,7 +193,8 @@ defmodule Pleroma.Mixfile do {:covertool, "~> 2.0", only: :test}, {:hackney, "~> 1.18.0", override: true}, {:mox, "~> 1.0", only: :test}, - {:websockex, "~> 0.4.3", only: :test} + {:websockex, "~> 0.4.3", only: :test}, + {:benchee, "~> 1.0", only: :benchmark} ] ++ oauth_deps() end @@ -4,11 +4,13 @@ "bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"}, "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, + "blurhash": {:hex, :rinpatch_blurhash, "0.1.0", "01a888b0f5f1f382ab52e4396f01831cbe8486ea5828604c90f4dac533d39a4b", [:mix], [{:mogrify, "~> 0.8.0", [hex: :mogrify, repo: "hexpm", optional: true]}], "hexpm", "19911a5dcbb0acb9710169a72f702bce6cb048822b12de566ccd82b2cc42b907"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"}, "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"}, - "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, + "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "90f6ce7672f70f56708792a98d98bd05176c9176", [ref: "90f6ce7672f70f56708792a98d98bd05176c9176"]}, "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, + "cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, @@ -22,18 +24,18 @@ "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, - "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "earmark": {:hex, :earmark, "1.4.22", "ea3e45c6359446dc308be0a64ce82a03260d973de7d0625a762e6d352ff57958", [:mix], [{:earmark_parser, "~> 1.4.23", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "1caf5145665a42fd76d5317286b0c171861fb1c04f86ab103dde76868814fdfb"}, "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, - "eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"}, - "ecto": {:hex, :ecto, "3.10.2", "6b887160281a61aa16843e47735b8a266caa437f80588c3ab80a8a960e6abe37", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6a895778f0d7648a4b34b486af59a1c8009041fbdf2b17f1ac215eb829c60235"}, + "eblurhash": {:git, "https://github.com/zotonic/eblurhash.git", "bc37ceb426ef021ee9927fb249bb93f7059194ab", [ref: "bc37ceb426ef021ee9927fb249bb93f7059194ab"]}, + "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, - "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.11", "6e20144c1446dcccfcdb4c142c9d8b7992a90a569b1d5958cbea5458550b25f0", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0 or ~> 0.17.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "def61f1f92d4f40d51c80bbae2157212d6c0a459eb604be446e47369cbd40b23"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, + "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.14", "7a20cfe913b0476542b43870e67386461258734896035e3f284039fd18bd4c4c", [:mix], [{:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "22f5f98592dd597db9416fcef00effae0787669fdcb6faf447e982b553798e98"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"}, "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"}, - "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, + "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "ex_aws": {:hex, :ex_aws, "2.1.9", "dc4865ecc20a05190a34a0ac5213e3e5e2b0a75a0c2835e923ae7bfeac5e3c31", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "3e6c776703c9076001fbe1f7c049535f042cb2afa0d2cbd3b47cbc4e92ac0d10"}, @@ -46,7 +48,7 @@ "fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"}, "fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "finch": {:hex, :finch, "0.10.2", "9ad27d68270d879f73f26604bb2e573d40f29bf0e907064a9a337f90a16a0312", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dd8b11b282072cec2ef30852283949c248bd5d2820c88d8acc89402b81db7550"}, + "finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"}, "flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"}, "floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"}, "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"}, @@ -59,7 +61,7 @@ "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, @@ -75,35 +77,36 @@ "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"}, - "mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"}, + "mogrify": {:hex, :mogrify, "0.8.0", "3506f3ca3f7b95a155f3b4ef803b5db176f5a0633723e3fe85e0d6399e3b11c8", [:mix], [], "hexpm", "2278d245f07056ea3b586e98801e933695147066fa4cf563f552c1b4f0ff8ad9"}, "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"}, - "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"}, + "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "oban": {:hex, :oban, "2.13.6", "a0cb1bce3bd393770512231fb5a3695fa19fd3af10d7575bf73f837aee7abf43", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c1c5eb16f377b3cbbf2ea14be24d20e3d91285af9d1ac86260b7c2af5464887"}, + "octo_fetch": {:hex, :octo_fetch, "0.3.0", "89ff501d2ac0448556ff1931634a538fe6d6cd358ba827ce1747e6a42a46efbf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "c07e44f2214ab153743b7b3182f380798d0b294b1f283811c1e30cff64096d3d"}, "open_api_spex": {:hex, :open_api_spex, "3.17.3", "ada8e352eb786050dd639db2439d3316e92f3798eb2abd051f55bb9af825b37e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "165db21a85ca83cffc8e7c8890f35b354eddda8255de7404a2848ed652b9f0fe"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, - "phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"}, + "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, - "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.5", "1495bb014be12c9a9252eca04b9af54246f6b5c1e4cd1f30210cd00ec540cf8e", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "ef4fa50dd78364409039c99cf6f98ab5209b4c5f8796c17f4db118324f0db852"}, + "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, + "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.14", "5ec615d4d61bf9d4755f158bd6c80372b715533fe6d6219e12d74fb5eedbeac1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "afeb6ba43ce329a6f7fc1c9acdfc6d3039995345f025febb7f409a92f6faebd3"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.19.5", "6e730595e8e9b8c5da230a814e557768828fd8dfeeb90377d2d8dbb52d4ec00a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b2eaa0dd3cfb9bd7fb949b88217df9f25aed915e986a28ad5c8a0d054e7ca9d3"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"}, - "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, - "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"}, - "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, + "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, + "phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"}, + "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.17.1", "01c29fd1205940ee55f7addb8f1dc25618ca63a8817e56fac4f6846fc2cddcbe", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "14b057b488e73be2beee508fb1955d8db90d6485c6466428fe9ccf1d6692a555"}, + "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, "pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"}, - "prom_ex": {:hex, :prom_ex, "1.7.1", "39331ee3fe6f9a8587d8208bf9274a253bb80281700e127dd18786cda5e08c37", [:mix], [{:absinthe, ">= 1.6.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.0.2", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.5.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.10.2", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.4.0", [hex: :oban, repo: "hexpm", optional: true]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.14.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.12.1", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.5.1", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.1", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0.2", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "4c978872b88a929833925a0f4d0561824804c671fdd04581e765509ed0a6ed08"}, + "prom_ex": {:hex, :prom_ex, "1.9.0", "63e6dda6c05cdeec1f26c48443dcc38ffd2118b3665ae8d2bd0e5b79f2aea03e", [:mix], [{:absinthe, ">= 1.6.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.0.2", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.5.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.4.0", [hex: :oban, repo: "hexpm", optional: true]}, {:octo_fetch, "~> 0.3", [hex: :octo_fetch, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.14.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.12.1", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.5", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry, ">= 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "01f3d4f69ec93068219e686cc65e58a29c42bea5429a8ff4e2121f19db178ee6"}, "prometheus": {:hex, :prometheus, "4.10.0", "792adbf0130ff61b5fa8826f013772af24b6e57b984445c8d602c8a0355704a1", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "2a99bb6dce85e238c7236fde6b0064f9834dc420ddbd962aac4ea2a3c3d59384"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex.git", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, @@ -114,6 +117,7 @@ "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "recon": {:hex, :recon, "2.5.3", "739107b9050ea683c30e96de050bc59248fd27ec147696f79a8797ff9fa17153", [:mix, :rebar3], [], "hexpm", "6c6683f46fd4a1dfd98404b9f78dcabc7fcd8826613a89dcb984727a8c3099d7"}, "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]}, + "rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"}, "sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, @@ -123,15 +127,19 @@ "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"}, "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, - "telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.0.2", "c98b1c580de637bfeac00db41b9fb91fb4c3548ee3d512a8ed7299172312eaf3", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "48351a0d56f80e38c997b44232b1043e0a081670d16766eee920e6254175b730"}, + "telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.1.0", "4e15f6d7dbedb3a4e3aed2262b7e1407f166fcb9c30ca3f96635dfbbef99965c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0dd10e7fe8070095df063798f82709b0a1224c31b8baf6278b423898d591a069"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"}, "timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"}, + "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, "tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"}, "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, + "vix": {:hex, :vix, "0.26.0", "027f10b6969b759318be84bd0bd8c88af877445e4e41cf96a0460392cea5399c", [:make, :mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "71b0a79ae7f199cacfc8e679b0e4ba25ee47dc02e182c5b9097efb29fbe14efd"}, "web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"}, "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, } diff --git a/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs b/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs new file mode 100644 index 000000000..d77db34cd --- /dev/null +++ b/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.AddQuoteUrlIndexToObjects do + use Ecto.Migration + @disable_ddl_transaction true + + def change do + create_if_not_exists( + index(:objects, ["(data->'quoteUrl')"], + name: :objects_quote_url, + concurrently: true + ) + ) + end +end diff --git a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs index 43bc7100b..580c38841 100644 --- a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs +++ b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs @@ -2,12 +2,20 @@ # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only +defmodule User do + use Ecto.Schema + + schema "users" do + field(:keys, :string) + field(:local, :boolean, default: true) + end +end + defmodule Pleroma.Repo.Migrations.GenerateUnsetUserKeys do use Ecto.Migration import Ecto.Query alias Pleroma.Keys alias Pleroma.Repo - alias Pleroma.User def change do query = diff --git a/priv/repo/migrations/20231107200724_consolidate_email_queues.exs b/priv/repo/migrations/20231107200724_consolidate_email_queues.exs new file mode 100644 index 000000000..63f5af369 --- /dev/null +++ b/priv/repo/migrations/20231107200724_consolidate_email_queues.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.ConsolidateEmailQueues do + use Ecto.Migration + + def change do + execute( + "UPDATE oban_jobs SET queue = 'mailer' WHERE queue in ('digest_emails', 'new_users_digest')" + ) + end +end diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex index 24a76263b..a75a6465d 100644 --- a/priv/scrubbers/default.ex +++ b/priv/scrubbers/default.ex @@ -36,30 +36,45 @@ defmodule Pleroma.HTML.Scrubber.Default do Meta.allow_tag_with_these_attributes(:a, ["name", "title", "lang"]) Meta.allow_tag_with_these_attributes(:abbr, ["title", "lang"]) + Meta.allow_tag_with_these_attributes(:acronym, ["title", "lang"]) - Meta.allow_tag_with_these_attributes(:b, ["lang"]) + # sort(1)-ed list Meta.allow_tag_with_these_attributes(:bdi, []) + Meta.allow_tag_with_these_attributes(:bdo, ["dir"]) + Meta.allow_tag_with_these_attributes(:big, ["lang"]) + Meta.allow_tag_with_these_attributes(:b, ["lang"]) Meta.allow_tag_with_these_attributes(:blockquote, ["lang"]) Meta.allow_tag_with_these_attributes(:br, ["lang"]) + Meta.allow_tag_with_these_attributes(:cite, ["lang"]) Meta.allow_tag_with_these_attributes(:code, ["lang"]) Meta.allow_tag_with_these_attributes(:del, ["lang"]) + Meta.allow_tag_with_these_attributes(:dfn, ["lang"]) Meta.allow_tag_with_these_attributes(:em, ["lang"]) Meta.allow_tag_with_these_attributes(:hr, ["lang"]) Meta.allow_tag_with_these_attributes(:i, ["lang"]) + Meta.allow_tag_with_these_attributes(:ins, ["lang"]) + Meta.allow_tag_with_these_attributes(:kbd, ["lang"]) Meta.allow_tag_with_these_attributes(:li, ["lang"]) Meta.allow_tag_with_these_attributes(:ol, ["lang"]) Meta.allow_tag_with_these_attributes(:p, ["lang"]) Meta.allow_tag_with_these_attributes(:pre, ["lang"]) - Meta.allow_tag_with_these_attributes(:strong, ["lang"]) - Meta.allow_tag_with_these_attributes(:sub, ["lang"]) - Meta.allow_tag_with_these_attributes(:sup, ["lang"]) - Meta.allow_tag_with_these_attributes(:ruby, ["lang"]) + Meta.allow_tag_with_these_attributes(:q, ["lang"]) Meta.allow_tag_with_these_attributes(:rb, ["lang"]) Meta.allow_tag_with_these_attributes(:rp, ["lang"]) - Meta.allow_tag_with_these_attributes(:rt, ["lang"]) Meta.allow_tag_with_these_attributes(:rtc, ["lang"]) + Meta.allow_tag_with_these_attributes(:rt, ["lang"]) + Meta.allow_tag_with_these_attributes(:ruby, ["lang"]) + Meta.allow_tag_with_these_attributes(:samp, ["lang"]) + Meta.allow_tag_with_these_attributes(:s, ["lang"]) + Meta.allow_tag_with_these_attributes(:small, ["lang"]) + Meta.allow_tag_with_these_attributes(:strong, ["lang"]) + Meta.allow_tag_with_these_attributes(:sub, ["lang"]) + Meta.allow_tag_with_these_attributes(:sup, ["lang"]) + Meta.allow_tag_with_these_attributes(:tt, ["lang"]) Meta.allow_tag_with_these_attributes(:u, ["lang"]) Meta.allow_tag_with_these_attributes(:ul, ["lang"]) + Meta.allow_tag_with_these_attributes(:var, ["lang"]) + Meta.allow_tag_with_these_attributes(:wbr, ["lang"]) Meta.allow_tag_with_this_attribute_values(:span, "class", [ "h-card", diff --git a/priv/scrubbers/search_indexing.ex b/priv/scrubbers/search_indexing.ex new file mode 100644 index 000000000..02756ab79 --- /dev/null +++ b/priv/scrubbers/search_indexing.ex @@ -0,0 +1,24 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTML.Scrubber.SearchIndexing do + @moduledoc """ + An HTML scrubbing policy that scrubs things for searching. + """ + + require FastSanitize.Sanitizer.Meta + alias FastSanitize.Sanitizer.Meta + + # Explicitly remove mentions + def scrub({:a, attrs, children}) do + if(Enum.any?(attrs, fn {att, val} -> att == "class" and String.contains?(val, "mention") end), + do: nil, + # Strip the tag itself, leave only children (text, presumably) + else: children + ) + end + + Meta.strip_comments() + Meta.strip_everything_not_covered() +end diff --git a/test/fixtures/png_with_transparency.png b/test/fixtures/png_with_transparency.png Binary files differnew file mode 100644 index 000000000..7963149db --- /dev/null +++ b/test/fixtures/png_with_transparency.png diff --git a/test/mix/tasks/pleroma/digest_test.exs b/test/mix/tasks/pleroma/digest_test.exs index d2a8606c7..08482aadb 100644 --- a/test/mix/tasks/pleroma/digest_test.exs +++ b/test/mix/tasks/pleroma/digest_test.exs @@ -23,6 +23,11 @@ defmodule Mix.Tasks.Pleroma.DigestTest do setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + describe "pleroma.digest test" do test "Sends digest to the given user" do user1 = insert(:user) diff --git a/test/mix/tasks/pleroma/user_test.exs b/test/mix/tasks/pleroma/user_test.exs index 4fdf6912b..c9bcf2951 100644 --- a/test/mix/tasks/pleroma/user_test.exs +++ b/test/mix/tasks/pleroma/user_test.exs @@ -20,6 +20,11 @@ defmodule Mix.Tasks.Pleroma.UserTest do import Mock import Pleroma.Factory + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + setup_all do Mix.shell(Mix.Shell.Process) diff --git a/test/pleroma/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs index d299fea63..36a6ca026 100644 --- a/test/pleroma/activity/ir/topics_test.exs +++ b/test/pleroma/activity/ir/topics_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Ir.TopicsTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase alias Pleroma.Activity alias Pleroma.Activity.Ir.Topics diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs index a48a68837..e38384c9c 100644 --- a/test/pleroma/activity_test.exs +++ b/test/pleroma/activity_test.exs @@ -145,7 +145,6 @@ defmodule Pleroma.ActivityTest do setup do: clear_config([:instance, :limit_to_local_content]) - @tag :skip_on_mac test "finds utf8 text in statuses", %{ japanese_activity: japanese_activity, user: user diff --git a/test/pleroma/conversation_test.exs b/test/pleroma/conversation_test.exs index 94897e7ea..809c1951a 100644 --- a/test/pleroma/conversation_test.exs +++ b/test/pleroma/conversation_test.exs @@ -13,6 +13,11 @@ defmodule Pleroma.ConversationTest do setup_all do: clear_config([:instance, :federating], true) + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + test "it goes through old direct conversations" do user = insert(:user) other_user = insert(:user) diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.ex b/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.exs index 226383c3c..760ecb465 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.ex +++ b/test/pleroma/ecto_type/activity_pub/object_validators/bare_uri_test.exs @@ -9,17 +9,17 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUriTest do test "diaspora://" do text = "diaspora://alice@fediverse.example/post/deadbeefdeadbeefdeadbeefdeadbeef" - assert {:ok, text} = BareUri.cast(text) + assert {:ok, ^text} = BareUri.cast(text) end test "nostr:" do text = "nostr:note1gwdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" - assert {:ok, text} = BareUri.cast(text) + assert {:ok, ^text} = BareUri.cast(text) end test "errors for non-URIs" do - assert :error == SafeText.cast(1) - assert :error == SafeText.cast("foo") - assert :error == SafeText.cast("foo bar") + assert :error == BareUri.cast(1) + assert :error == BareUri.cast("foo") + assert :error == BareUri.cast("foo bar") end end diff --git a/test/pleroma/http/adapter_helper/hackney_test.exs b/test/pleroma/http/adapter_helper/hackney_test.exs index 35d6c49a9..57ce4728c 100644 --- a/test/pleroma/http/adapter_helper/hackney_test.exs +++ b/test/pleroma/http/adapter_helper/hackney_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers alias Pleroma.HTTP.AdapterHelper.Hackney diff --git a/test/pleroma/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs index a769f9362..6a718be21 100644 --- a/test/pleroma/instances/instance_test.exs +++ b/test/pleroma/instances/instance_test.exs @@ -31,14 +31,6 @@ defmodule Pleroma.Instances.InstanceTest do assert {:ok, instance} = Instance.set_reachable(instance.host) refute instance.unreachable_since end - - test "does NOT create an Instance record in case of no existing matching record" do - host = "domain.org" - assert nil == Instance.set_reachable(host) - - assert [] = Repo.all(Ecto.Query.from(i in Instance)) - assert Instance.reachable?(host) - end end describe "set_unreachable/1" do diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index 71af9acb8..4cf14e65b 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -21,6 +21,11 @@ defmodule Pleroma.NotificationTest do alias Pleroma.Web.Push alias Pleroma.Web.Streamer + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + describe "create_notifications" do test "never returns nil" do user = insert(:user) diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index 7bc5c9928..2025d93e4 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.ObjectTest do use Oban.Testing, repo: Pleroma.Repo import ExUnit.CaptureLog + import Mox import Pleroma.Factory import Tesla.Mock @@ -15,10 +16,12 @@ defmodule Pleroma.ObjectTest do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.CommonAPI setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + ConfigMock |> stub_with(Pleroma.Test.StaticConfig) :ok end diff --git a/test/pleroma/scheduled_activity_test.exs b/test/pleroma/scheduled_activity_test.exs index 3a0927d3f..4818e8bcf 100644 --- a/test/pleroma/scheduled_activity_test.exs +++ b/test/pleroma/scheduled_activity_test.exs @@ -3,19 +3,23 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ScheduledActivityTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.ScheduledActivity + alias Pleroma.Test.StaticConfig + alias Pleroma.UnstubbedConfigMock, as: ConfigMock + import Mox import Pleroma.Factory - setup do: clear_config([ScheduledActivity, :enabled]) - - setup [:ensure_local_uploader] - describe "creation" do test "scheduled activities with jobs when ScheduledActivity enabled" do - clear_config([ScheduledActivity, :enabled], true) + ConfigMock + |> stub(:get, fn + [ScheduledActivity, :enabled] -> true + path -> StaticConfig.get(path) + end) + user = insert(:user) today = @@ -34,7 +38,12 @@ defmodule Pleroma.ScheduledActivityTest do end test "scheduled activities without jobs when ScheduledActivity disabled" do - clear_config([ScheduledActivity, :enabled], false) + ConfigMock + |> stub(:get, fn + [ScheduledActivity, :enabled] -> false + path -> StaticConfig.get(path) + end) + user = insert(:user) today = @@ -53,6 +62,9 @@ defmodule Pleroma.ScheduledActivityTest do end test "when daily user limit is exceeded" do + ConfigMock + |> stub_with(StaticConfig) + user = insert(:user) today = @@ -69,6 +81,9 @@ defmodule Pleroma.ScheduledActivityTest do end test "when total user limit is exceeded" do + ConfigMock + |> stub_with(StaticConfig) + user = insert(:user) today = @@ -89,6 +104,9 @@ defmodule Pleroma.ScheduledActivityTest do end test "when scheduled_at is earlier than 5 minute from now" do + ConfigMock + |> stub_with(StaticConfig) + user = insert(:user) scheduled_at = diff --git a/test/pleroma/activity/search_test.exs b/test/pleroma/search/database_search_test.exs index 3b5fd2c3c..6c47ff425 100644 --- a/test/pleroma/activity/search_test.exs +++ b/test/pleroma/search/database_search_test.exs @@ -2,8 +2,8 @@ # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Activity.SearchTest do - alias Pleroma.Activity.Search +defmodule Pleroma.Search.DatabaseSearchTest do + alias Pleroma.Search.DatabaseSearch, as: Search alias Pleroma.Web.CommonAPI import Pleroma.Factory diff --git a/test/pleroma/search/meilisearch_test.exs b/test/pleroma/search/meilisearch_test.exs new file mode 100644 index 000000000..eea454323 --- /dev/null +++ b/test/pleroma/search/meilisearch_test.exs @@ -0,0 +1,160 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Search.MeilisearchTest do + require Pleroma.Constants + + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + import Tesla.Mock + import Mox + + alias Pleroma.Search.Meilisearch + alias Pleroma.UnstubbedConfigMock, as: Config + alias Pleroma.Web.CommonAPI + alias Pleroma.Workers.SearchIndexingWorker + + describe "meilisearch" do + test "indexes a local post on creation" do + user = insert(:user) + + Tesla.Mock.mock(fn + %{ + method: :put, + url: "http://127.0.0.1:7700/indexes/objects/documents", + body: body + } -> + assert match?( + [%{"content" => "guys i just don't wanna leave the swamp"}], + Jason.decode!(body) + ) + + # To make sure that the worker is called + send(self(), "posted_to_meilisearch") + + %{ + "enqueuedAt" => "2023-11-12T12:36:46.927517Z", + "indexUid" => "objects", + "status" => "enqueued", + "taskUid" => 6, + "type" => "documentAdditionOrUpdate" + } + |> json() + end) + + Config + |> expect(:get, 3, fn + [Pleroma.Search, :module], nil -> + Meilisearch + + [Pleroma.Search.Meilisearch, :url], nil -> + "http://127.0.0.1:7700" + + [Pleroma.Search.Meilisearch, :private_key], nil -> + "secret" + end) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "guys i just don't wanna leave the swamp", + visibility: "public" + }) + + args = %{"op" => "add_to_index", "activity" => activity.id} + + assert_enqueued( + worker: SearchIndexingWorker, + args: args + ) + + assert :ok = perform_job(SearchIndexingWorker, args) + assert_received("posted_to_meilisearch") + end + + test "doesn't index posts that are not public" do + user = insert(:user) + + Enum.each(["private", "direct"], fn visibility -> + {:ok, activity} = + CommonAPI.post(user, %{ + status: "guys i just don't wanna leave the swamp", + visibility: visibility + }) + + args = %{"op" => "add_to_index", "activity" => activity.id} + + Config + |> expect(:get, fn + [Pleroma.Search, :module], nil -> + Meilisearch + end) + + assert_enqueued(worker: SearchIndexingWorker, args: args) + assert :ok = perform_job(SearchIndexingWorker, args) + end) + end + + test "deletes posts from index when deleted locally" do + user = insert(:user) + + Tesla.Mock.mock(fn + %{ + method: :put, + url: "http://127.0.0.1:7700/indexes/objects/documents", + body: body + } -> + assert match?( + [%{"content" => "guys i just don't wanna leave the swamp"}], + Jason.decode!(body) + ) + + %{ + "enqueuedAt" => "2023-11-12T12:36:46.927517Z", + "indexUid" => "objects", + "status" => "enqueued", + "taskUid" => 6, + "type" => "documentAdditionOrUpdate" + } + |> json() + + %{method: :delete, url: "http://127.0.0.1:7700/indexes/objects/documents/" <> id} -> + send(self(), "called_delete") + assert String.length(id) > 1 + json(%{}) + end) + + Config + |> expect(:get, 6, fn + [Pleroma.Search, :module], nil -> + Meilisearch + + [Pleroma.Search.Meilisearch, :url], nil -> + "http://127.0.0.1:7700" + + [Pleroma.Search.Meilisearch, :private_key], nil -> + "secret" + end) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "guys i just don't wanna leave the swamp", + visibility: "public" + }) + + args = %{"op" => "add_to_index", "activity" => activity.id} + assert_enqueued(worker: SearchIndexingWorker, args: args) + assert :ok = perform_job(SearchIndexingWorker, args) + + {:ok, _} = CommonAPI.delete(activity.id, user) + + delete_args = %{"op" => "remove_from_index", "object" => activity.object.id} + assert_enqueued(worker: SearchIndexingWorker, args: delete_args) + assert :ok = perform_job(SearchIndexingWorker, delete_args) + + assert_received("called_delete") + end + end +end diff --git a/test/pleroma/signature_test.exs b/test/pleroma/signature_test.exs index b849cbee7..f5a915fa8 100644 --- a/test/pleroma/signature_test.exs +++ b/test/pleroma/signature_test.exs @@ -43,10 +43,7 @@ defmodule Pleroma.SignatureTest do end test "it returns error when not found user" do - assert capture_log(fn -> - assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == - {:error, :error} - end) =~ "[error] Could not decode user" + assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == {:error, :error} end test "it returns error if public key is nil" do diff --git a/test/pleroma/upload/filter/analyze_metadata_test.exs b/test/pleroma/upload/filter/analyze_metadata_test.exs index b800a4a43..e4ac673b2 100644 --- a/test/pleroma/upload/filter/analyze_metadata_test.exs +++ b/test/pleroma/upload/filter/analyze_metadata_test.exs @@ -20,6 +20,20 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadataTest do assert meta.blurhash end + test "it blurhashes images with an alpha component" do + upload = %Pleroma.Upload{ + name: "an… image.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/png_with_transparency.png"), + tempfile: Path.absname("test/fixtures/png_with_transparency.png") + } + + {:ok, :filtered, meta} = AnalyzeMetadata.filter(upload) + + assert %{width: 320, height: 320} = meta + assert meta.blurhash == "eXJi-E:SwCEm5rCmn$+YWYn+15K#5A$xxCi{SiV]s*W:Efa#s.jE-T" + end + test "adds the dimensions for videos" do upload = %Pleroma.Upload{ name: "coolvideo.mp4", diff --git a/test/pleroma/upload/filter/exiftool/read_description_test.exs b/test/pleroma/upload/filter/exiftool/read_description_test.exs index 7cc83969d..9a1bd61d7 100644 --- a/test/pleroma/upload/filter/exiftool/read_description_test.exs +++ b/test/pleroma/upload/filter/exiftool/read_description_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase alias Pleroma.Upload.Filter @uploads %Pleroma.Upload{ diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs index 6584c2def..facb634c3 100644 --- a/test/pleroma/upload_test.exs +++ b/test/pleroma/upload_test.exs @@ -6,10 +6,19 @@ defmodule Pleroma.UploadTest do use Pleroma.DataCase import ExUnit.CaptureLog + import Mox + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Upload alias Pleroma.Uploaders.Uploader + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + @upload_file %Plug.Upload{ content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), @@ -236,6 +245,8 @@ defmodule Pleroma.UploadTest do describe "Setting a custom base_url for uploaded media" do setup do: clear_config([Pleroma.Upload, :base_url], "https://cache.pleroma.social") + # This seems to be backwards. Skipped for that reason + @tag skip: true test "returns a media url with configured base_url" do base_url = Pleroma.Config.get([Pleroma.Upload, :base_url]) diff --git a/test/pleroma/uploaders/s3_test.exs b/test/pleroma/uploaders/s3_test.exs index d870449b1..b8df0e65a 100644 --- a/test/pleroma/uploaders/s3_test.exs +++ b/test/pleroma/uploaders/s3_test.exs @@ -3,22 +3,27 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.S3Test do - use Pleroma.DataCase + use Pleroma.DataCase, async: true + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Uploaders.S3 + alias Pleroma.Uploaders.S3.ExAwsMock - import Mock + import Mox import ExUnit.CaptureLog - setup do - clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) - clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") - clear_config([Pleroma.Uploaders.S3]) - clear_config([Pleroma.Uploaders.S3, :bucket], "test_bucket") - end - describe "get_file/1" do - test "it returns path to local folder for files" do + test "it returns url for files" do + ConfigMock + |> expect(:get, 6, fn key -> + [ + {Pleroma.Upload, + [uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]}, + {Pleroma.Uploaders.S3, [bucket: "test_bucket"]} + ] + |> get_in(key) + end) + assert S3.get_file("test_image.jpg") == { :ok, {:url, "https://s3.amazonaws.com/test_bucket/test_image.jpg"} @@ -26,13 +31,16 @@ defmodule Pleroma.Uploaders.S3Test do end test "it returns path without bucket when truncated_namespace set to ''" do - clear_config([Pleroma.Uploaders.S3], - bucket: "test_bucket", - bucket_namespace: "myaccount", - truncated_namespace: "" - ) - - clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") + ConfigMock + |> expect(:get, 6, fn key -> + [ + {Pleroma.Upload, + [uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]}, + {Pleroma.Uploaders.S3, + [bucket: "test_bucket", truncated_namespace: "", bucket_namespace: "myaccount"]} + ] + |> get_in(key) + end) assert S3.get_file("test_image.jpg") == { :ok, @@ -41,10 +49,15 @@ defmodule Pleroma.Uploaders.S3Test do end test "it returns path with bucket namespace when namespace is set" do - clear_config([Pleroma.Uploaders.S3], - bucket: "test_bucket", - bucket_namespace: "family" - ) + ConfigMock + |> expect(:get, 6, fn key -> + [ + {Pleroma.Upload, + [uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]}, + {Pleroma.Uploaders.S3, [bucket: "test_bucket", bucket_namespace: "family"]} + ] + |> get_in(key) + end) assert S3.get_file("test_image.jpg") == { :ok, @@ -62,28 +75,42 @@ defmodule Pleroma.Uploaders.S3Test do tempfile: Path.absname("test/instance_static/add/shortcode.png") } + ConfigMock + |> expect(:get, fn [Pleroma.Uploaders.S3] -> + [ + bucket: "test_bucket" + ] + end) + [file_upload: file_upload] end test "save file", %{file_upload: file_upload} do - with_mock ExAws, request: fn _ -> {:ok, :ok} end do - assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}} - end + ExAwsMock + |> expect(:request, fn _req -> {:ok, %{status_code: 200}} end) + + assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}} end test "returns error", %{file_upload: file_upload} do - with_mock ExAws, request: fn _ -> {:error, "S3 Upload failed"} end do - assert capture_log(fn -> - assert S3.put_file(file_upload) == {:error, "S3 Upload failed"} - end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}" - end + ExAwsMock + |> expect(:request, fn _req -> {:error, "S3 Upload failed"} end) + + assert capture_log(fn -> + assert S3.put_file(file_upload) == {:error, "S3 Upload failed"} + end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}" end end describe "delete_file/1" do - test_with_mock "deletes file", ExAws, request: fn _req -> {:ok, %{status_code: 204}} end do + test "deletes file" do + ExAwsMock + |> expect(:request, fn _req -> {:ok, %{status_code: 204}} end) + + ConfigMock + |> expect(:get, fn [Pleroma.Uploaders.S3, :bucket] -> "test_bucket" end) + assert :ok = S3.delete_file("image.jpg") - assert_called(ExAws.request(:_)) end end end diff --git a/test/pleroma/user/backup_async_test.exs b/test/pleroma/user/backup_async_test.exs new file mode 100644 index 000000000..76716d684 --- /dev/null +++ b/test/pleroma/user/backup_async_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.BackupAsyncTest do + use Pleroma.DataCase, async: true + + import Pleroma.Factory + import Mox + + alias Pleroma.UnstubbedConfigMock, as: ConfigMock + alias Pleroma.User.Backup + alias Pleroma.User.Backup.ProcessorMock + + setup do + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + {:ok, backup} = user |> Backup.new() |> Repo.insert() + %{backup: backup} + end + + @tag capture_log: true + test "it handles unrecoverable exceptions", %{backup: backup} do + ProcessorMock + |> expect(:do_process, fn _, _ -> + raise "mock exception" + end) + + ConfigMock + |> stub_with(Pleroma.Config) + + {:error, %{backup: backup, reason: :exit}} = Backup.process(backup, ProcessorMock) + + assert backup.state == :failed + end + + @tag capture_log: true + test "it handles timeouts", %{backup: backup} do + ProcessorMock + |> expect(:do_process, fn _, _ -> + Process.sleep(:timer.seconds(4)) + end) + + ConfigMock + |> expect(:get, fn [Pleroma.User.Backup, :process_wait_time] -> :timer.seconds(2) end) + + {:error, %{backup: backup, reason: :timeout}} = Backup.process(backup, ProcessorMock) + + assert backup.state == :failed + end +end diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs index 066bf6ba8..0ac57e334 100644 --- a/test/pleroma/user/backup_test.exs +++ b/test/pleroma/user/backup_test.exs @@ -9,10 +9,14 @@ defmodule Pleroma.User.BackupTest do import Mock import Pleroma.Factory import Swoosh.TestAssertions + import Mox alias Pleroma.Bookmark alias Pleroma.Tests.ObanHelpers + alias Pleroma.UnstubbedConfigMock, as: ConfigMock + alias Pleroma.Uploaders.S3.ExAwsMock alias Pleroma.User.Backup + alias Pleroma.User.Backup.ProcessorMock alias Pleroma.Web.CommonAPI alias Pleroma.Workers.BackupWorker @@ -20,6 +24,14 @@ defmodule Pleroma.User.BackupTest do clear_config([Pleroma.Upload, :uploader]) clear_config([Backup, :limit_days]) clear_config([Pleroma.Emails.Mailer, :enabled], true) + + ConfigMock + |> stub_with(Pleroma.Config) + + ProcessorMock + |> stub_with(Pleroma.User.Backup.Processor) + + :ok end test "it does not requrie enabled email" do @@ -302,24 +314,6 @@ defmodule Pleroma.User.BackupTest do end end - test "it handles unrecoverable exceptions" do - user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) - - assert {:ok, backup} = user |> Backup.new() |> Repo.insert() - - with_mock Backup, [:passthrough], do_process: fn _, _ -> raise "mock exception" end do - {:error, %{backup: backup, reason: :exit}} = Backup.process(backup) - - assert backup.state == :failed - end - - with_mock Backup, [:passthrough], do_process: fn _, _ -> Process.sleep(:timer.seconds(32)) end do - {:error, %{backup: backup, reason: :timeout}} = Backup.process(backup) - - assert backup.state == :failed - end - end - describe "it uploads and deletes a backup archive" do setup do clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") @@ -345,14 +339,14 @@ defmodule Pleroma.User.BackupTest do clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) clear_config([Pleroma.Uploaders.S3, :streaming_enabled], false) - with_mock ExAws, - request: fn - %{http_method: :put} -> {:ok, :ok} - %{http_method: :delete} -> {:ok, %{status_code: 204}} - end do - assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path) - assert {:ok, _backup} = Backup.delete(backup) - end + ExAwsMock + |> expect(:request, 2, fn + %{http_method: :put} -> {:ok, :ok} + %{http_method: :delete} -> {:ok, %{status_code: 204}} + end) + + assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path) + assert {:ok, _backup} = Backup.delete(backup) end test "Local", %{path: path, backup: backup} do diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 7f60b959a..77ca9198b 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -19,6 +19,11 @@ defmodule Pleroma.UserTest do import ExUnit.CaptureLog import Swoosh.TestAssertions + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok @@ -1946,8 +1951,8 @@ defmodule Pleroma.UserTest do end end - test "get_public_key_for_ap_id fetches a user that's not in the db" do - assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") + test "get_public_key_for_ap_id returns correctly for user that's not in the db" do + assert :error = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") end describe "per-user rich-text filtering" do diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 62eb9b5a3..0dc61c2e5 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -25,6 +25,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do require Pleroma.Constants + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 1e8c14043..a024e8d0f 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do alias Pleroma.Config alias Pleroma.Notification alias Pleroma.Object + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils @@ -19,11 +20,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do import ExUnit.CaptureLog import Mock + import Mox import Pleroma.Factory import Tesla.Mock setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + :ok end @@ -770,6 +776,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 end + + test "increates quotes count", %{user: user} do + user2 = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) + ap_id = activity.data["id"] + quote_data = %{status: "1", quote_id: activity.id} + + # public + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "public")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 1 + + # unlisted + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "unlisted")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + + # private + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "private")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + + # direct + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "direct")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + end end describe "fetch activities for recipients" do @@ -2653,6 +2687,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert user.name == " " end + @tag capture_log: true test "pin_data_from_featured_collection will ignore unsupported values" do assert %{} == ActivityPub.pin_data_from_featured_collection(%{ diff --git a/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs index 248190034..a70e3c1a2 100644 --- a/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs index 6557c3a98..0da3afa3b 100644 --- a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs @@ -7,10 +7,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do use Pleroma.Tests.Helpers alias Pleroma.HTTP + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy import Mock + import Mox @message %{ "type" => "Create", @@ -42,6 +44,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do } } + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + setup do: clear_config([:media_proxy, :enabled], true) test "it prefetches media proxy URIs" do diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 8d14e976a..4ad45c818 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRFTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers alias Pleroma.Web.ActivityPub.MRF diff --git a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs index 77f2044e9..3b8a2df86 100644 --- a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs @@ -5,9 +5,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do use Pleroma.DataCase, async: true + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + import Mox import Pleroma.Factory describe "attachments" do @@ -116,6 +118,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) {:ok, attachment} = diff --git a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs index 8192efe97..812944452 100644 --- a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs @@ -5,11 +5,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do use Pleroma.DataCase alias Pleroma.Object + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator alias Pleroma.Web.CommonAPI + import Mox import Pleroma.Factory describe "chat message create activities" do @@ -82,6 +84,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) valid_chat_message = @@ -103,6 +108,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) valid_chat_message = @@ -124,6 +132,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) valid_chat_message = diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 5e58d75db..9c5983347 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -128,10 +128,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do message = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!() - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - object = Object.normalize(activity) - assert [%{"type" => "Mention"}, %{"type" => "Link"}] = object.data["tag"] + assert capture_log(fn -> + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + object = Object.normalize(activity) + assert [%{"type" => "Mention"}, %{"type" => "Link"}] = object.data["tag"] + end) =~ "Error while fetching" end test "it accepts quote posts" do diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index 3f93c872b..9ca21f5d9 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -16,6 +16,41 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do require Pleroma.Constants + describe "strip_report_status_data/1" do + test "does not break on issues with the reported activites" do + reporter = insert(:user) + target_account = insert(:user) + {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"}) + context = Utils.generate_context_id() + content = "foobar" + post_id = activity.data["id"] + + res = + Utils.make_flag_data( + %{ + actor: reporter, + context: context, + account: target_account, + statuses: [%{"id" => post_id}], + content: content + }, + %{} + ) + + res = + res + |> Map.put("object", res["object"] ++ [nil, 1, 5, "123"]) + + {:ok, activity} = Pleroma.Web.ActivityPub.ActivityPub.insert(res) + + [user_id, object | _] = activity.data["object"] + + {:ok, stripped} = Utils.strip_report_status_data(activity) + + assert stripped.data["object"] == [user_id, object["id"]] + end + end + describe "fetch the latest Follow" do test "fetches the latest Follow activity" do %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs index e1ab50542..a7ee8359d 100644 --- a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs @@ -15,6 +15,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -1077,6 +1078,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "/api/pleroma/backups" do test "it creates a backup", %{conn: conn} do + ConfigMock + |> Mox.stub_with(Pleroma.Config) + admin = %{id: admin_id, nickname: admin_nickname} = insert(:user, is_admin: true) token = insert(:oauth_admin_token, user: admin) user = %{id: user_id, nickname: user_nickname} = insert(:user) diff --git a/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs index 852334a57..de9c20145 100644 --- a/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -5,9 +5,11 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do use Pleroma.Web.ConnCase - import Pleroma.Factory import Mock + import Mox + import Pleroma.Factory + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.MediaProxy setup do: clear_config([:media_proxy]) @@ -128,6 +130,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do "http://example.com/media/fb1f4d.jpg" ] + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + with_mocks [ {MediaProxy.Invalidation.Script, [], [ @@ -150,6 +155,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do "http://example.com/media/fb1f4d.jpg" ] + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do conn |> put_req_header("content-type", "application/json") diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index bb9dcb4aa..8edfda54c 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -19,6 +19,11 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do alias Pleroma.Web.Endpoint alias Pleroma.Web.MediaProxy + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index b21dd4e23..98b922b52 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -12,6 +12,7 @@ defmodule Pleroma.Web.CommonAPITest do alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier @@ -20,9 +21,10 @@ defmodule Pleroma.Web.CommonAPITest do alias Pleroma.Web.CommonAPI alias Pleroma.Workers.PollWorker - import Pleroma.Factory - import Mock import Ecto.Query, only: [from: 2] + import Mock + import Mox + import Pleroma.Factory require Pleroma.Constants @@ -31,6 +33,13 @@ defmodule Pleroma.Web.CommonAPITest do :ok end + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + setup do: clear_config([:instance, :safe_dm_mentions]) setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) diff --git a/test/pleroma/web/endpoint/metrics_exporter_test.exs b/test/pleroma/web/endpoint/metrics_exporter_test.exs deleted file mode 100644 index ad236d4cb..000000000 --- a/test/pleroma/web/endpoint/metrics_exporter_test.exs +++ /dev/null @@ -1,69 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Endpoint.MetricsExporterTest do - # Modifies AppEnv, has to stay synchronous - use Pleroma.Web.ConnCase - - alias Pleroma.Web.Endpoint.MetricsExporter - - defp config do - Application.get_env(:prometheus, MetricsExporter) - end - - describe "with default config" do - test "does NOT expose app metrics", %{conn: conn} do - conn - |> get(config()[:path]) - |> json_response(404) - end - end - - describe "when enabled" do - setup do - initial_config = config() - on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end) - - Application.put_env( - :prometheus, - MetricsExporter, - Keyword.put(initial_config, :enabled, true) - ) - end - - test "serves app metrics", %{conn: conn} do - conn = get(conn, config()[:path]) - assert response = response(conn, 200) - - for metric <- [ - "http_requests_total", - "http_request_duration_microseconds", - "phoenix_controller_call_duration", - "telemetry_scrape_duration", - "erlang_vm_memory_atom_bytes_total" - ] do - assert response =~ ~r/#{metric}/ - end - end - - test "when IP whitelist configured, " <> - "serves app metrics only if client IP is whitelisted", - %{conn: conn} do - Application.put_env( - :prometheus, - MetricsExporter, - Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255']) - ) - - conn - |> get(config()[:path]) - |> json_response(404) - - conn - |> Map.put(:remote_ip, {127, 127, 127, 127}) - |> get(config()[:path]) - |> response(200) - end - end -end diff --git a/test/pleroma/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs index 6d11d4f37..ed34d6490 100644 --- a/test/pleroma/web/fallback_test.exs +++ b/test/pleroma/web/fallback_test.exs @@ -6,20 +6,6 @@ defmodule Pleroma.Web.FallbackTest do use Pleroma.Web.ConnCase import Pleroma.Factory - describe "neither preloaded data nor metadata attached to" do - test "GET /registration/:token", %{conn: conn} do - response = get(conn, "/registration/foo") - - assert html_response(response, 200) =~ "<!--server-generated-meta-->" - end - - test "GET /*path", %{conn: conn} do - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" - end - end - test "GET /*path adds a title", %{conn: conn} do clear_config([:instance, :name], "a cool title") @@ -29,21 +15,28 @@ defmodule Pleroma.Web.FallbackTest do end describe "preloaded data and metadata attached to" do - test "GET /:maybe_nickname_or_id", %{conn: conn} do + test "GET /:maybe_nickname_or_id with existing user", %{conn: conn} do clear_config([:instance, :name], "a cool title") - user = insert(:user) - user_missing = get(conn, "/foo") - user_present = get(conn, "/#{user.nickname}") - assert html_response(user_missing, 200) =~ "<!--server-generated-meta-->" - refute html_response(user_present, 200) =~ "<!--server-generated-meta-->" - assert html_response(user_present, 200) =~ "initial-results" - assert html_response(user_present, 200) =~ "<title>a cool title</title>" + resp = get(conn, "/#{user.nickname}") + + assert html_response(resp, 200) =~ "<title>a cool title</title>" + refute html_response(resp, 200) =~ "<!--server-generated-meta-->" + assert html_response(resp, 200) =~ "initial-results" + end + + test "GET /:maybe_nickname_or_id with missing user", %{conn: conn} do + clear_config([:instance, :name], "a cool title") + + resp = get(conn, "/foo") + + assert html_response(resp, 200) =~ "<title>a cool title</title>" + refute html_response(resp, 200) =~ "initial-results" end test "GET /*path", %{conn: conn} do - assert conn + refute conn |> get("/foo") |> html_response(200) =~ "<!--server-generated-meta-->" @@ -65,10 +58,12 @@ defmodule Pleroma.Web.FallbackTest do end test "GET /main/all", %{conn: conn} do + clear_config([:instance, :name], "a cool title") public_page = get(conn, "/main/all") refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" assert html_response(public_page, 200) =~ "initial-results" + assert html_response(public_page, 200) =~ "<title>a cool title</title>" end end diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 128e60b0a..d8e5f9d39 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -18,6 +18,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do import Pleroma.Factory + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + describe "account fetching" do test "works by id" do %User{id: user_id} = insert(:user) diff --git a/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs index f90ef96f9..40b23a5d6 100644 --- a/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.DirectoryControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Web.CommonAPI import Pleroma.Factory diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index 750296230..b92fd8afa 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -6,8 +6,10 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do use Pleroma.Web.ConnCase import ExUnit.CaptureLog + import Mox alias Pleroma.Object + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -15,6 +17,9 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do setup do: oauth_access(["write:media"]) setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + image = %Plug.Upload{ content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), @@ -145,6 +150,9 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do setup do: oauth_access(["write:media"]) setup %{user: actor} do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + file = %Plug.Upload{ content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), @@ -177,6 +185,9 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do setup do: oauth_access(["read:media"]) setup %{user: actor} do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + file = %Plug.Upload{ content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), diff --git a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs index 1524df98f..350b935d7 100644 --- a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs @@ -12,6 +12,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do import Pleroma.Factory + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + test "does NOT render account/pleroma/relationship by default" do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) diff --git a/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs index 21f2ea6f5..632242221 100644 --- a/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -3,15 +3,25 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Repo alias Pleroma.ScheduledActivity + alias Pleroma.UnstubbedConfigMock, as: ConfigMock - import Pleroma.Factory import Ecto.Query + import Mox + import Pleroma.Factory - setup do: clear_config([ScheduledActivity, :enabled]) + setup do + ConfigMock + |> stub(:get, fn + [ScheduledActivity, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) + + :ok + end test "shows scheduled activities" do %{user: user, conn: conn} = oauth_access(["read:statuses"]) @@ -55,7 +65,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do end test "updates a scheduled activity" do - clear_config([ScheduledActivity, :enabled], true) %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) @@ -103,7 +112,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do end test "deletes a scheduled activity" do - clear_config([ScheduledActivity, :enabled], true) %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) diff --git a/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs index 0a9240b70..3654c6b20 100644 --- a/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs @@ -13,6 +13,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do import Tesla.Mock import Mock + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + setup_all do mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok @@ -37,7 +42,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do end end - @tag :skip_on_mac test "search", %{conn: conn} do user = insert(:user) user_two = insert(:user, %{nickname: "shp@shitposter.club"}) diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index de3b52e26..4e3d34172 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -19,25 +19,38 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do alias Pleroma.Web.CommonAPI alias Pleroma.Workers.ScheduledActivityWorker + import Mox import Pleroma.Factory setup do: clear_config([:instance, :federating]) setup do: clear_config([:instance, :allow_relay]) - setup do: clear_config([:rich_media, :enabled]) setup do: clear_config([:mrf, :policies]) setup do: clear_config([:mrf_keyword, :reject]) + setup do + Pleroma.UnstubbedConfigMock + |> stub_with(Pleroma.Config) + + Pleroma.StaticStubbedConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> false + path -> Pleroma.Test.StaticConfig.get(path) + end) + + :ok + end + describe "posting statuses" do setup do: oauth_access(["write:statuses"]) test "posting a status does not increment reblog_count when relaying", %{conn: conn} do clear_config([:instance, :federating], true) - Config.get([:instance, :allow_relay], true) + clear_config([:instance, :allow_relay], true) response = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{ + |> post("/api/v1/statuses", %{ "content_type" => "text/plain", "source" => "Pleroma FE", "status" => "Hello world", @@ -50,7 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn - |> get("api/v1/statuses/#{response["id"]}", %{}) + |> get("/api/v1/statuses/#{response["id"]}", %{}) |> json_response_and_validate_schema(200) assert response["reblogs_count"] == 0 @@ -109,7 +122,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn_four = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{ + |> post("/api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) @@ -156,7 +169,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"error" => "Expiry date is too soon"} = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{ + |> post("/api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) @@ -168,7 +181,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"error" => "Expiry date is too soon"} = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{ + |> post("/api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) @@ -182,7 +195,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{"status" => "GNO/Linux"}) + |> post("/api/v1/statuses", %{"status" => "GNO/Linux"}) |> json_response_and_validate_schema(422) end @@ -316,7 +329,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "fake statuses' preview card is not cached", %{conn: conn} do - clear_config([:rich_media, :enabled], true) + Pleroma.StaticStubbedConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) Tesla.Mock.mock(fn %{ @@ -353,7 +370,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "posting a status with OGP link preview", %{conn: conn} do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - clear_config([:rich_media, :enabled], true) + + Pleroma.StaticStubbedConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) conn = conn @@ -375,7 +397,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"}) + |> post("/api/v1/statuses", %{"status" => content, "visibility" => "direct"}) assert %{"id" => id} = response = json_response_and_validate_schema(conn, 200) assert response["visibility"] == "direct" @@ -412,7 +434,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do result = conn - |> get("api/v1/statuses/#{activity}") + |> get("/api/v1/statuses/#{activity}") assert %{ "content" => "cofe is my copilot", @@ -441,7 +463,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do result = conn - |> get("api/v1/statuses/#{activity}") + |> get("/api/v1/statuses/#{activity}") assert %{ "content" => "club mate is my wingman", @@ -1644,7 +1666,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"id" => id} = conn |> put_req_header("content-type", "application/json") - |> post("api/v1/statuses", %{ + |> post("/api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) @@ -1684,7 +1706,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do describe "cards" do setup do - clear_config([:rich_media, :enabled], true) + Pleroma.StaticStubbedConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) oauth_access(["read:statuses"]) end @@ -1894,7 +1920,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn |> assign(:user, user3) |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"])) - |> get("api/v1/timelines/home") + |> get("/api/v1/timelines/home") [reblogged_activity] = json_response_and_validate_schema(conn3, 200) diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index b13a8033b..c120dd53c 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -527,7 +527,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) # Only direct should be visible here - res_conn = get(conn_user_two, "api/v1/timelines/direct") + res_conn = get(conn_user_two, "/api/v1/timelines/direct") assert [status] = json_response_and_validate_schema(res_conn, :ok) @@ -539,14 +539,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do build_conn() |> assign(:user, user_one) |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) - |> get("api/v1/timelines/direct") + |> get("/api/v1/timelines/direct") [status] = json_response_and_validate_schema(res_conn, :ok) assert %{"visibility" => "direct"} = status # Both should be visible here - res_conn = get(conn_user_two, "api/v1/timelines/home") + res_conn = get(conn_user_two, "/api/v1/timelines/home") [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok) @@ -559,14 +559,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do }) end) - res_conn = get(conn_user_two, "api/v1/timelines/direct") + res_conn = get(conn_user_two, "/api/v1/timelines/direct") statuses = json_response_and_validate_schema(res_conn, :ok) assert length(statuses) == 20 max_id = List.last(statuses)["id"] - res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") + res_conn = get(conn_user_two, "/api/v1/timelines/direct?max_id=#{max_id}") assert [status] = json_response_and_validate_schema(res_conn, :ok) @@ -591,7 +591,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do visibility: "direct" }) - res_conn = get(conn, "api/v1/timelines/direct") + res_conn = get(conn, "/api/v1/timelines/direct") [status] = json_response_and_validate_schema(res_conn, :ok) assert status["id"] == direct.id diff --git a/test/pleroma/web/mastodon_api/mastodon_api_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_test.exs index 250a20352..190c13611 100644 --- a/test/pleroma/web/mastodon_api/mastodon_api_test.exs +++ b/test/pleroma/web/mastodon_api/mastodon_api_test.exs @@ -7,11 +7,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do alias Pleroma.Notification alias Pleroma.ScheduledActivity + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.MastodonAPI import Pleroma.Factory + import Mox describe "follow/3" do test "returns error when followed user is deactivated" do @@ -88,6 +90,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do describe "get_scheduled_activities/2" do test "returns user scheduled activities" do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + user = insert(:user) today = diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 45412bb34..fc8b79536 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -4,13 +4,22 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do alias Pleroma.Repo + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User use Pleroma.Web.ConnCase import Mock + import Mox import Pleroma.Factory + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + describe "updating credentials" do setup do: oauth_access(["write:accounts"]) setup :request_content_type diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 3bb4970ca..8dcdaf447 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -5,11 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase, async: false + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView + import Mox import Pleroma.Factory import Tesla.Mock @@ -35,7 +37,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do inserted_at: ~N[2017-08-15 15:47:06.597036], emoji: %{"karjalanpiirakka" => "/file.png"}, raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"", - also_known_as: ["https://shitposter.zone/users/shp"] + also_known_as: ["https://shitposter.zone/users/shp"], + last_status_at: NaiveDateTime.utc_now() }) expected = %{ @@ -74,7 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do fields: [] }, fqn: "shp@shitposter.club", - last_status_at: nil, + last_status_at: user.last_status_at |> NaiveDateTime.to_date() |> Date.to_iso8601(), pleroma: %{ ap_id: user.ap_id, also_known_as: ["https://shitposter.zone/users/shp"], @@ -752,6 +755,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do clear_config([:media_proxy, :enabled], true) clear_config([:media_preview_proxy, :enabled]) + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + user = insert(:user, avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]}, @@ -759,7 +765,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do emoji: %{"joker_smile" => "https://evil.website/society.png"} ) - with media_preview_enabled <- [false, true] do + Enum.each([true, false], fn media_preview_enabled -> clear_config([:media_preview_proxy, :enabled], media_preview_enabled) AccountView.render("show.json", %{user: user, skip_visibility_check: true}) @@ -777,7 +783,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do true end) |> assert() - end + end) end test "renders mute expiration date" do diff --git a/test/pleroma/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs index ddbe4557f..47425d2a9 100644 --- a/test/pleroma/web/mastodon_api/views/notification_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/notification_view_test.exs @@ -22,6 +22,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView import Pleroma.Factory + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + defp test_notifications_rendering(notifications, user, expected_result) do result = NotificationView.render("index.json", %{notifications: notifications, for: user}) diff --git a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs index 07a65a3bc..30b38c6c5 100644 --- a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs @@ -4,12 +4,16 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do use Pleroma.DataCase, async: true + alias Pleroma.ScheduledActivity + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.ScheduledActivityView alias Pleroma.Web.MastodonAPI.StatusView + + import Mox import Pleroma.Factory test "A scheduled activity with a media attachment" do @@ -27,6 +31,9 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) attrs = %{ diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index 0bc232556..b31955891 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -337,7 +337,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do thread_muted: false, emoji_reactions: [], parent_visible: false, - pinned_at: nil + pinned_at: nil, + quotes_count: 0 } } diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs index 9ce092fd8..5b3f5fbdc 100644 --- a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs +++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs @@ -9,9 +9,17 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do import Mox alias Pleroma.ReverseProxy.ClientMock + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.MediaProxy alias Plug.Conn + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + describe "Media Proxy" do setup do clear_config([:media_proxy, :enabled], true) diff --git a/test/pleroma/web/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs index ffab1247f..718892665 100644 --- a/test/pleroma/web/media_proxy_test.exs +++ b/test/pleroma/web/media_proxy_test.exs @@ -7,9 +7,19 @@ defmodule Pleroma.Web.MediaProxyTest do use Pleroma.Tests.Helpers alias Pleroma.Config + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.Endpoint alias Pleroma.Web.MediaProxy + import Mox + + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + defp decode_result(encoded) do {:ok, decoded} = MediaProxy.decode_url(encoded) decoded @@ -222,7 +232,12 @@ defmodule Pleroma.Web.MediaProxyTest do test "ensure Pleroma.Upload base_url is always whitelisted" do media_url = "https://media.pleroma.social" - clear_config([Pleroma.Upload, :base_url], media_url) + + ConfigMock + |> stub(:get, fn + [Pleroma.Upload, :base_url] -> media_url + path -> Pleroma.Test.StaticConfig.get(path) + end) url = "#{media_url}/static/logo.png" encoded = MediaProxy.url(url) diff --git a/test/pleroma/web/metadata/providers/open_graph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs index b7ce95f7d..6a0fc9b10 100644 --- a/test/pleroma/web/metadata/providers/open_graph_test.exs +++ b/test/pleroma/web/metadata/providers/open_graph_test.exs @@ -4,9 +4,19 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do use Pleroma.DataCase + import Mox import Pleroma.Factory + + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.Metadata.Providers.OpenGraph + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) test "it renders all supported types of attachments and skips unknown types" do diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs index f41d6a322..83a08d9fc 100644 --- a/test/pleroma/web/o_auth/o_auth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -186,7 +186,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert html_response(conn, 302) assert redirected_to(conn) == app.redirect_uris - assert get_flash(conn, :error) == "Failed to authenticate: (error description)." + assert conn.assigns.flash["error"] == "Failed to authenticate: (error description)." end test "GET /oauth/registration_details renders registration details form", %{ @@ -307,7 +307,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do |> post("/oauth/register", bad_params) assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/ - assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken." + assert conn.assigns.flash["error"] == "Error: #{bad_param} has already been taken." end end @@ -398,7 +398,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do |> post("/oauth/register", params) assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/ - assert get_flash(conn, :error) == "Invalid Username/Password" + assert conn.assigns.flash["error"] == "Invalid Username/Password" end end diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index 36e581f5e..3e8fcd956 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -196,7 +196,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> get("/notice/#{like_activity.id}") |> response(200) - assert resp =~ "<!--server-generated-meta-->" + refute resp =~ ~r(<meta content="[^"]*" property="og:url") end test "404s a private notice", %{conn: conn} do diff --git a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs index a758925b7..21e619fa4 100644 --- a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs @@ -5,12 +5,17 @@ defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do use Pleroma.Web.ConnCase + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User.Backup alias Pleroma.Web.PleromaAPI.BackupView setup do clear_config([Pleroma.Upload, :uploader]) clear_config([Backup, :limit_days]) + + ConfigMock + |> Mox.stub_with(Pleroma.Config) + oauth_access(["read:backups"]) end diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs index aa40c6f44..7138a6636 100644 --- a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -7,10 +7,12 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do alias Pleroma.Chat alias Pleroma.Chat.MessageReference alias Pleroma.Object + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI + import Mox import Pleroma.Factory describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do @@ -112,6 +114,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) other_user = insert(:user) diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs index 21e7d4839..8c2dcc1bb 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -13,6 +13,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do import Pleroma.Factory + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do user = insert(:user) other_user = insert(:user) diff --git a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs index 365d26ab1..02afeda67 100644 --- a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs @@ -26,6 +26,8 @@ defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do constant_unreachable: constant_unreachable, constant: constant } do + clear_config([:instance, :public], false) + constant_host = URI.parse(constant).host assert conn diff --git a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs index b72569d4b..81f09cdd1 100644 --- a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs @@ -5,8 +5,11 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do use Pleroma.Web.ConnCase, async: true + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User + import Mox + test "mascot upload" do %{conn: conn} = oauth_access(["write:accounts"]) @@ -29,6 +32,9 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + conn = conn |> put_req_header("content-type", "multipart/form-data") @@ -53,6 +59,9 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + ret_conn = conn |> put_req_header("content-type", "multipart/form-data") diff --git a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs index 908ce962d..be94a02ad 100644 --- a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -18,7 +18,8 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do "title" => "lain radio episode 1", "artist" => "lain", "album" => "lain radio", - "length" => "180000" + "length" => "180000", + "externalLink" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1" }) assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200) @@ -33,21 +34,24 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do CommonAPI.listen(user, %{ title: "lain radio episode 1", artist: "lain", - album: "lain radio" + album: "lain radio", + externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1" }) {:ok, _activity} = CommonAPI.listen(user, %{ title: "lain radio episode 2", artist: "lain", - album: "lain radio" + album: "lain radio", + externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+2" }) {:ok, _activity} = CommonAPI.listen(user, %{ title: "lain radio episode 3", artist: "lain", - album: "lain radio" + album: "lain radio", + externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+3" }) conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles") diff --git a/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs new file mode 100644 index 000000000..f942f0556 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.StatusControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "getting quotes of a specified post" do + setup do + [current_user, user] = insert_pair(:user) + %{user: current_user, conn: conn} = oauth_access(["read:statuses"], user: current_user) + [current_user: current_user, user: user, conn: conn] + end + + test "shows quotes of a post", %{conn: conn} do + user = insert(:user) + activity = insert(:note_activity) + + {:ok, quote_post} = CommonAPI.post(user, %{status: "quoat", quote_id: activity.id}) + + response = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/quotes") + |> json_response_and_validate_schema(:ok) + + [status] = response + + assert length(response) == 1 + assert status["id"] == quote_post.id + end + + test "returns 404 error when a post can't be seen", %{conn: conn} do + activity = insert(:direct_note_activity) + + response = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/quotes") + + assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"} + end + + test "returns 404 error when a post does not exist", %{conn: conn} do + response = + conn + |> get("/api/v1/pleroma/statuses/idontexist/quotes") + + assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"} + end + end +end diff --git a/test/pleroma/web/pleroma_api/views/backup_view_test.exs b/test/pleroma/web/pleroma_api/views/backup_view_test.exs index 6908463d6..b125b8872 100644 --- a/test/pleroma/web/pleroma_api/views/backup_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/backup_view_test.exs @@ -4,10 +4,21 @@ defmodule Pleroma.Web.PleromaAPI.BackupViewTest do use Pleroma.DataCase, async: true + + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User.Backup alias Pleroma.Web.PleromaAPI.BackupView + + import Mox import Pleroma.Factory + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + test "it renders the ID" do user = insert(:user) backup = Backup.new(user) diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs index 7ab3f5acd..44d40269c 100644 --- a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs @@ -3,21 +3,28 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do - use Pleroma.DataCase + alias Pleroma.NullCache + use Pleroma.DataCase, async: true alias Pleroma.Chat alias Pleroma.Chat.MessageReference alias Pleroma.Object + alias Pleroma.StaticStubbedConfigMock + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + import Mox import Pleroma.Factory test "it displays a chat message" do user = insert(:user) recipient = insert(:user) + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + file = %Plug.Upload{ content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), @@ -35,6 +42,14 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do cm_ref = MessageReference.for_chat_and_object(chat, object) + id = cm_ref.id + + Pleroma.CachexMock + |> stub(:get, fn + :chat_message_id_idempotency_key_cache, ^id -> {:ok, "123"} + cache, key -> NullCache.get(cache, key) + end) + chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref) assert chat_message[:id] == cm_ref.id @@ -46,7 +61,11 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do assert match?([%{shortcode: "firefox"}], chat_message[:emojis]) assert chat_message[:idempotency_key] == "123" - clear_config([:rich_media, :enabled], true) + StaticStubbedConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) Tesla.Mock.mock_global(fn %{url: "https://example.com/ogp"} -> diff --git a/test/pleroma/web/plugs/ensure_privileged_plug_test.exs b/test/pleroma/web/plugs/ensure_privileged_plug_test.exs index 4b6679b66..bba972fad 100644 --- a/test/pleroma/web/plugs/ensure_privileged_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_privileged_plug_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsurePrivilegedPlugTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Web.Plugs.EnsurePrivilegedPlug import Pleroma.Factory diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index ab31c5f22..6f4d24d9e 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -4,7 +4,11 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do use Pleroma.Web.ConnCase + import Mock + import Mox + + alias Pleroma.UnstubbedConfigMock, as: ConfigMock @dir "test/tmp/instance_static" @@ -66,6 +70,9 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do File.mkdir_p!("#{path}/proxy/rr/ss") File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image") + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + url = Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large") @@ -82,6 +89,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do "api", "main", "ostatus_subscribe", + "authorize_interaction", "oauth", "objects", "activities", diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs index 8323ff6ab..6a9366e28 100644 --- a/test/pleroma/web/plugs/uploaded_media_plug_test.exs +++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs @@ -4,10 +4,18 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do use Pleroma.Web.ConnCase, async: true + + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Upload + import Mox + defp upload_file(context) do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + Pleroma.DataCase.ensure_local_uploader(context) + File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ @@ -23,6 +31,13 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do setup_all :upload_file + setup do + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + + :ok + end + test "does not send Content-Disposition header when name param is not set", %{ attachment_url: attachment_url } do diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs index 2eee0acd9..3ceea3d71 100644 --- a/test/pleroma/web/push/impl_test.exs +++ b/test/pleroma/web/push/impl_test.exs @@ -5,10 +5,12 @@ defmodule Pleroma.Web.Push.ImplTest do use Pleroma.DataCase, async: true + import Mox import Pleroma.Factory alias Pleroma.Notification alias Pleroma.Object + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI @@ -257,6 +259,9 @@ defmodule Pleroma.Web.Push.ImplTest do filename: "an_image.jpg" } + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id) diff --git a/test/pleroma/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs index 630b3ca95..3ef5705ce 100644 --- a/test/pleroma/web/rich_media/helpers_test.exs +++ b/test/pleroma/web/rich_media/helpers_test.exs @@ -3,22 +3,31 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.HelpersTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true + alias Pleroma.StaticStubbedConfigMock, as: ConfigMock alias Pleroma.Web.CommonAPI alias Pleroma.Web.RichMedia.Helpers + import Mox import Pleroma.Factory import Tesla.Mock setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + ConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> false + path -> Pleroma.Test.StaticConfig.get(path) + end) + |> stub(:get, fn + path, default -> Pleroma.Test.StaticConfig.get(path, default) + end) + :ok end - setup do: clear_config([:rich_media, :enabled]) - test "refuses to crawl incomplete URLs" do user = insert(:user) @@ -28,7 +37,11 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do content_type: "text/markdown" }) - clear_config([:rich_media, :enabled], true) + ConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end @@ -42,7 +55,11 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do content_type: "text/markdown" }) - clear_config([:rich_media, :enabled], true) + ConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end @@ -56,12 +73,18 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do content_type: "text/markdown" }) - clear_config([:rich_media, :enabled], true) + ConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) assert %{page_url: "https://example.com/ogp", rich_media: _} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end + # This does not seem to work. The urls are being fetched. + @tag skip: true test "refuses to crawl URLs of private network from posts" do user = insert(:user) @@ -73,7 +96,11 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"}) {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"}) - clear_config([:rich_media, :enabled], true) + ConfigMock + |> stub(:get, fn + [:rich_media, :enabled] -> true + path -> Pleroma.Test.StaticConfig.get(path) + end) assert %{} = Helpers.fetch_data_for_activity(activity) assert %{} = Helpers.fetch_data_for_activity(activity2) diff --git a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs index 1194e0afe..41f8ebcd7 100644 --- a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs +++ b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs @@ -3,16 +3,18 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.MFA alias Pleroma.MFA.TOTP + alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.User alias Pleroma.Web.CommonAPI + import Ecto.Query import ExUnit.CaptureLog + import Mox import Pleroma.Factory - import Ecto.Query setup_all do: clear_config([:instance, :federating], true) setup do: clear_config([:user, :deny_follow_blocked]) @@ -429,6 +431,9 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do test "with media proxy" do clear_config([:media_proxy, :enabled], true) + ConfigMock + |> stub_with(Pleroma.Test.StaticConfig) + user = insert(:user, %{ local: false, @@ -455,4 +460,38 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do assert avatar_url == "#{Pleroma.Web.Endpoint.url()}/localuser/avatar.png" end end + + describe "GET /authorize_interaction - authorize_interaction/2" do + test "redirects to /ostatus_subscribe", %{conn: conn} do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + + %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "emelie") + } + end) + + conn = + conn + |> get( + remote_follow_path(conn, :authorize_interaction, %{ + uri: "https://mastodon.social/users/emelie" + }) + ) + + assert redirected_to(conn) == + remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}) + end + end end diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index a4da23635..d06ae71aa 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -106,7 +106,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do |> get("/api/pleroma/healthcheck") |> json_response_and_validate_schema(503) - assert response == %{} + assert response == %{"error" => "Healthcheck disabled"} end test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do diff --git a/test/pleroma/workers/cron/digest_emails_worker_test.exs b/test/pleroma/workers/cron/digest_emails_worker_test.exs index 851f4d63a..e0bdf303e 100644 --- a/test/pleroma/workers/cron/digest_emails_worker_test.exs +++ b/test/pleroma/workers/cron/digest_emails_worker_test.exs @@ -14,6 +14,11 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do setup do: clear_config([:email_notifications, :digest]) setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + + setup do clear_config([:email_notifications, :digest], %{ active: true, inactivity_threshold: 7, diff --git a/test/pleroma/workers/cron/new_users_digest_worker_test.exs b/test/pleroma/workers/cron/new_users_digest_worker_test.exs index 84914876c..0e4234cc8 100644 --- a/test/pleroma/workers/cron/new_users_digest_worker_test.exs +++ b/test/pleroma/workers/cron/new_users_digest_worker_test.exs @@ -10,6 +10,11 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do alias Pleroma.Web.CommonAPI alias Pleroma.Workers.Cron.NewUsersDigestWorker + setup do + Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config) + :ok + end + test "it sends new users digest emails" do yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) admin = insert(:user, %{is_admin: true}) diff --git a/test/pleroma/workers/purge_expired_token_test.exs b/test/pleroma/workers/purge_expired_token_test.exs index d891eb8bb..add572bb8 100644 --- a/test/pleroma/workers/purge_expired_token_test.exs +++ b/test/pleroma/workers/purge_expired_token_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredTokenTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase use Oban.Testing, repo: Pleroma.Repo import Pleroma.Factory diff --git a/test/pleroma/workers/receiver_worker_test.exs b/test/pleroma/workers/receiver_worker_test.exs index acea0ae00..b9b6d6af2 100644 --- a/test/pleroma/workers/receiver_worker_test.exs +++ b/test/pleroma/workers/receiver_worker_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ReceiverWorkerTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase use Oban.Testing, repo: Pleroma.Repo import Mock diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 3c9cab061..14403f0b8 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -115,6 +115,7 @@ defmodule Pleroma.DataCase do Mox.stub_with(Pleroma.Web.ActivityPub.ActivityPubMock, Pleroma.Web.ActivityPub.ActivityPub) Mox.stub_with(Pleroma.Web.FederatorMock, Pleroma.Web.Federator) Mox.stub_with(Pleroma.ConfigMock, Pleroma.Config) + Mox.stub_with(Pleroma.StaticStubbedConfigMock, Pleroma.Test.StaticConfig) end def ensure_local_uploader(context) do diff --git a/test/support/mocks.ex b/test/support/mocks.ex index d167996bd..d906f0e1d 100644 --- a/test/support/mocks.ex +++ b/test/support/mocks.ex @@ -26,5 +26,11 @@ Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock, Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing) Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting) +Mox.defmock(Pleroma.UnstubbedConfigMock, for: Pleroma.Config.Getting) +Mox.defmock(Pleroma.StaticStubbedConfigMock, for: Pleroma.Config.Getting) Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging) + +Mox.defmock(Pleroma.User.Backup.ProcessorMock, for: Pleroma.User.Backup.ProcessorAPI) + +Mox.defmock(Pleroma.Uploaders.S3.ExAwsMock, for: Pleroma.Uploaders.S3.ExAwsAPI) diff --git a/test/test_helper.exs b/test/test_helper.exs index 7727cffbc..27b777d5f 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -4,8 +4,7 @@ Code.put_compiler_option(:warnings_as_errors, true) -os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: [] -ExUnit.start(exclude: [:federated, :erratic] ++ os_exclude) +ExUnit.start(exclude: [:federated, :erratic]) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual) @@ -18,3 +17,16 @@ ExUnit.after_suite(fn _results -> uploads = Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads], "test/uploads") File.rm_rf!(uploads) end) + +defmodule Pleroma.Test.StaticConfig do + @moduledoc """ + This module provides a Config that is completely static, built at startup time from the environment. It's safe to use in testing as it will not modify any state. + """ + + @behaviour Pleroma.Config.Getting + @config Application.get_all_env(:pleroma) + + def get(path, default \\ nil) do + get_in(@config, path) || default + end +end diff --git a/tools/check-changelog b/tools/check-changelog index 60692033f..d053ed577 100644 --- a/tools/check-changelog +++ b/tools/check-changelog @@ -6,7 +6,7 @@ git remote add upstream https://git.pleroma.social/pleroma/pleroma.git git fetch upstream ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}:refs/remotes/upstream/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME git diff --raw --no-renames upstream/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME HEAD -- changelog.d | \ - grep ' A\t' | grep '\.\(skip\|add\|remove\|fix\|security\)$' + grep ' A\t' | grep '\.\(skip\|add\|remove\|fix\|security\|change\)$' ret=$? if [ $ret -eq 0 ]; then |