diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd0c5c8d4..9a754ed78 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -229,7 +229,7 @@ arm: only: *release-only tags: - arm32 - image: elixir:1.10.3 + image: arm32v7/elixir:1.10.3 cache: *release-cache variables: *release-variables before_script: *before-release @@ -241,7 +241,7 @@ arm-musl: only: *release-only tags: - arm32 - image: elixir:1.10.3-alpine + image: arm32v7/elixir:1.10.3-alpine cache: *release-cache variables: *release-variables before_script: *before-release-musl @@ -253,7 +253,7 @@ arm64: only: *release-only tags: - arm - image: elixir:1.10.3 + image: arm64v8/elixir:1.10.3 cache: *release-cache variables: *release-variables before_script: *before-release @@ -265,8 +265,7 @@ arm64-musl: only: *release-only tags: - arm - # TODO: Replace with upstream image when 1.9.0 comes out - image: elixir:1.10.3-alpine + image: arm64v8/elixir:1.10.3-alpine cache: *release-cache variables: *release-variables before_script: *before-release-musl diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ff9a9f0..8658d5440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending. - Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances. - Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute. +- Admin API: An endpoint to manage frontends @@ -41,11 +42,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased (Patch) + +### Changed + +- Fix ability to update Pleroma Chat push notifications with PUT /api/v1/push/subscription and alert type pleroma:chat_mention + ### Fixed - Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`. - S3 Uploads with Elixir 1.11. - Fixed Emoji Reaction activity filtering from blocked and muted accounts +- Mix task pleroma.user delete_activities for source installations. ## [2.2.0] - 2020-11-12 diff --git a/config/config.exs b/config/config.exs index 0b8a75aad..1ac140ed0 100644 --- a/config/config.exs +++ b/config/config.exs @@ -129,7 +129,6 @@ dispatch: [ {:_, [ - {"/api/fedsocket/v1", Pleroma.Web.FedSockets.IncomingHandler, []}, {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, {"/websocket", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, diff --git a/config/description.exs b/config/description.exs index 0552b37e0..a663d8127 100644 --- a/config/description.exs +++ b/config/description.exs @@ -272,19 +272,6 @@ } ] }, - %{ - group: :pleroma, - key: :fed_sockets, - type: :group, - description: "Websocket based federation", - children: [ - %{ - key: :enabled, - type: :boolean, - description: "Enable FedSockets" - } - ] - }, %{ group: :pleroma, key: Pleroma.Emails.Mailer, diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index f7b5bcae7..19ac6a65f 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -1499,3 +1499,66 @@ Returns the content of the document "url": "https://example.com/instance/panel.html" } ``` + +## `GET /api/pleroma/admin/frontends + +### List available frontends + +- Response: + +```json +[ + { + "build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build", + "git": "https://git.pleroma.social/pleroma/fedi-fe", + "installed": true, + "name": "fedi-fe", + "ref": "master" + }, + { + "build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build", + "git": "https://git.pleroma.social/lambadalambda/kenoma", + "installed": false, + "name": "kenoma", + "ref": "master" + } +] +``` + +## `POST /api/pleroma/admin/frontends/install` + +### Install a frontend + +- Params: + - `name`: frontend name, required + - `ref`: frontend ref + - `file`: path to a frontend zip file + - `build_url`: build URL + - `build_dir`: build directory + +- Response: + +```json +[ + { + "build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build", + "git": "https://git.pleroma.social/pleroma/fedi-fe", + "installed": true, + "name": "fedi-fe", + "ref": "master" + }, + { + "build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build", + "git": "https://git.pleroma.social/lambadalambda/kenoma", + "installed": false, + "name": "kenoma", + "ref": "master" + } +] +``` + +```json +{ + "error": "Could not install frontend" +} +``` diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 17999da55..843496482 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -233,7 +233,7 @@ Post here request with `grant_type=refresh_token` to obtain new access token. Re `POST /api/v1/accounts` -Has theses additional parameters (which are the same as in Pleroma-API): +Has these additional parameters (which are the same as in Pleroma-API): - `fullname`: optional - `bio`: optional @@ -261,6 +261,16 @@ Has theses additional parameters (which are the same as in Pleroma-API): - `pleroma.metadata.post_formats`: A list of the allowed post format types - `vapid_public_key`: The public key needed for push messages +## Push Subscription + +`POST /api/v1/push/subscription` +`PUT /api/v1/push/subscription` + +Permits these additional alert types: + +- pleroma:chat_mention +- pleroma:emoji_reaction + ## Markers Has these additional fields under the `pleroma` object: diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index ebf95ebc9..4d18ac30a 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -220,18 +220,6 @@ config :pleroma, :mrf_user_allowlist, %{ * `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`) * `enabled`: whether scheduled activities are sent to the job queue to be executed -## FedSockets -FedSockets is an experimental feature allowing for Pleroma backends to federate using a persistant websocket connection as opposed to making each federation a seperate http connection. This feature is currently off by default. It is configurable throught he following options. - -### :fedsockets -* `enabled`: Enables FedSockets for this instance. `false` by default. -* `connection_duration`: Time an idle websocket is kept open. -* `rejection_duration`: Failures to connect via FedSockets will not be retried for this period of time. -* `fed_socket_fetches` and `fed_socket_rejections`: Settings passed to `cachex` for the fetch registry, and rejection stacks. See `Pleroma.Web.FedSockets` for more details. - - -## Frontends - ### :frontend_configurations This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options). diff --git a/installation/pleroma.nginx b/installation/pleroma.nginx index d613befd2..9890cb2b1 100644 --- a/installation/pleroma.nginx +++ b/installation/pleroma.nginx @@ -93,9 +93,4 @@ server { chunked_transfer_encoding on; proxy_pass http://phoenix; } - - location /api/fedsocket/v1 { - proxy_request_buffering off; - proxy_pass http://phoenix/api/fedsocket/v1; - } } diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 49ba2aae4..3de11efce 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -14,7 +14,7 @@ defmodule Mix.Pleroma do :swoosh, :timex ] - @cachex_children ["object", "user", "scrubber"] + @cachex_children ["object", "user", "scrubber", "web_resp"] @doc "Common functions to be reused in mix tasks" def start_pleroma do Pleroma.Config.Holder.save_default() diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex index cbce81ab9..f15dbc38b 100644 --- a/lib/mix/tasks/pleroma/frontend.ex +++ b/lib/mix/tasks/pleroma/frontend.ex @@ -17,8 +17,6 @@ def run(["install", "none" | _args]) do end def run(["install", frontend | args]) do - log_level = Logger.level() - Logger.configure(level: :warn) start_pleroma() {options, [], []} = @@ -33,109 +31,6 @@ def run(["install", frontend | args]) do ] ) - instance_static_dir = - with nil <- options[:static_dir] do - Pleroma.Config.get!([:instance, :static_dir]) - end - - cmd_frontend_info = %{ - "name" => frontend, - "ref" => options[:ref], - "build_url" => options[:build_url], - "build_dir" => options[:build_dir] - } - - config_frontend_info = Pleroma.Config.get([:frontends, :available, frontend], %{}) - - frontend_info = - Map.merge(config_frontend_info, cmd_frontend_info, fn _key, config, cmd -> - # This only overrides things that are actually set - cmd || config - end) - - ref = frontend_info["ref"] - - unless ref do - raise "No ref given or configured" - end - - dest = - Path.join([ - instance_static_dir, - "frontends", - frontend, - ref - ]) - - fe_label = "#{frontend} (#{ref})" - - tmp_dir = Path.join([instance_static_dir, "frontends", "tmp"]) - - with {_, :ok} <- - {:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, options[:file])}, - shell_info("Installing #{fe_label} to #{dest}"), - :ok <- install_frontend(frontend_info, tmp_dir, dest) do - File.rm_rf!(tmp_dir) - shell_info("Frontend #{fe_label} installed to #{dest}") - - Logger.configure(level: log_level) - else - {:download_or_unzip, _} -> - shell_info("Could not download or unzip the frontend") - - _e -> - shell_info("Could not install the frontend") - end - end - - defp download_or_unzip(frontend_info, temp_dir, file) do - if file do - with {:ok, zip} <- File.read(Path.expand(file)) do - unzip(zip, temp_dir) - end - else - download_build(frontend_info, temp_dir) - end - end - - def unzip(zip, dest) do - with {:ok, unzipped} <- :zip.unzip(zip, [:memory]) do - File.rm_rf!(dest) - File.mkdir_p!(dest) - - Enum.each(unzipped, fn {filename, data} -> - path = filename - - new_file_path = Path.join(dest, path) - - new_file_path - |> Path.dirname() - |> File.mkdir_p!() - - File.write!(new_file_path, data) - end) - - :ok - end - end - - defp download_build(frontend_info, dest) do - shell_info("Downloading pre-built bundle for #{frontend_info["name"]}") - url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"]) - - with {:ok, %{status: 200, body: zip_body}} <- - Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do - unzip(zip_body, dest) - else - e -> {:error, e} - end - end - - defp install_frontend(frontend_info, source, dest) do - from = frontend_info["build_dir"] || "dist" - File.rm_rf!(dest) - File.mkdir_p!(dest) - File.cp_r!(Path.join([source, from]), dest) - :ok + Pleroma.Frontend.install(frontend, options) end end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 7c4cd9626..8f08a6222 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -207,8 +207,7 @@ defp dont_run_in_test(_) do name: Pleroma.Web.Streamer.registry(), keys: :duplicate, partitions: System.schedulers_online() - ]}, - Pleroma.Web.FedSockets.Supervisor + ]} ] end diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex new file mode 100644 index 000000000..bf935a728 --- /dev/null +++ b/lib/pleroma/frontend.ex @@ -0,0 +1,110 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Frontend do + alias Pleroma.Config + + require Logger + + def install(name, opts \\ []) do + frontend_info = %{ + "ref" => opts[:ref], + "build_url" => opts[:build_url], + "build_dir" => opts[:build_dir] + } + + frontend_info = + [:frontends, :available, name] + |> Config.get(%{}) + |> Map.merge(frontend_info, fn _key, config, cmd -> + # This only overrides things that are actually set + cmd || config + end) + + ref = frontend_info["ref"] + + unless ref do + raise "No ref given or configured" + end + + dest = Path.join([dir(), name, ref]) + + label = "#{name} (#{ref})" + tmp_dir = Path.join(dir(), "tmp") + + with {_, :ok} <- + {:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, opts[:file])}, + Logger.info("Installing #{label} to #{dest}"), + :ok <- install_frontend(frontend_info, tmp_dir, dest) do + File.rm_rf!(tmp_dir) + Logger.info("Frontend #{label} installed to #{dest}") + else + {:download_or_unzip, _} -> + Logger.info("Could not download or unzip the frontend") + {:error, "Could not download or unzip the frontend"} + + _e -> + Logger.info("Could not install the frontend") + {:error, "Could not install the frontend"} + end + end + + def dir(opts \\ []) do + if is_nil(opts[:static_dir]) do + Pleroma.Config.get!([:instance, :static_dir]) + else + opts[:static_dir] + end + |> Path.join("frontends") + end + + defp download_or_unzip(frontend_info, temp_dir, nil), + do: download_build(frontend_info, temp_dir) + + defp download_or_unzip(_frontend_info, temp_dir, file) do + with {:ok, zip} <- File.read(Path.expand(file)) do + unzip(zip, temp_dir) + end + end + + def unzip(zip, dest) do + with {:ok, unzipped} <- :zip.unzip(zip, [:memory]) do + File.rm_rf!(dest) + File.mkdir_p!(dest) + + Enum.each(unzipped, fn {filename, data} -> + path = filename + + new_file_path = Path.join(dest, path) + + new_file_path + |> Path.dirname() + |> File.mkdir_p!() + + File.write!(new_file_path, data) + end) + end + end + + defp download_build(frontend_info, dest) do + Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}") + url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"]) + + with {:ok, %{status: 200, body: zip_body}} <- + Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do + unzip(zip_body, dest) + else + {:error, e} -> {:error, e} + e -> {:error, e} + end + end + + defp install_frontend(frontend_info, source, dest) do + from = frontend_info["build_dir"] || "dist" + File.rm_rf!(dest) + File.mkdir_p!(dest) + File.cp_r!(Path.join([source, from]), dest) + :ok + end +end diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index ae4301738..20d8f687d 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -12,7 +12,6 @@ defmodule Pleroma.Object.Fetcher do alias Pleroma.Web.ActivityPub.ObjectValidator alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.Federator - alias Pleroma.Web.FedSockets require Logger require Pleroma.Constants @@ -183,16 +182,16 @@ defp maybe_date_fetch(headers, date) do end end - def fetch_and_contain_remote_object_from_id(prm, opts \\ []) + def fetch_and_contain_remote_object_from_id(id) - def fetch_and_contain_remote_object_from_id(%{"id" => id}, opts), - do: fetch_and_contain_remote_object_from_id(id, opts) + def fetch_and_contain_remote_object_from_id(%{"id" => id}), + do: fetch_and_contain_remote_object_from_id(id) - def fetch_and_contain_remote_object_from_id(id, opts) when is_binary(id) do + def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do Logger.debug("Fetching object #{id} via AP") with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")}, - {:ok, body} <- get_object(id, opts), + {:ok, body} <- get_object(id), {:ok, data} <- safe_json_decode(body), :ok <- Containment.contain_origin_from_id(id, data) do {:ok, data} @@ -208,22 +207,10 @@ def fetch_and_contain_remote_object_from_id(id, opts) when is_binary(id) do end end - def fetch_and_contain_remote_object_from_id(_id, _opts), + def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"} - defp get_object(id, opts) do - with false <- Keyword.get(opts, :force_http, false), - {:ok, fedsocket} <- FedSockets.get_or_create_fed_socket(id) do - Logger.debug("fetching via fedsocket - #{inspect(id)}") - FedSockets.fetch(fedsocket, id) - else - _other -> - Logger.debug("fetching via http - #{inspect(id)}") - get_object_http(id) - end - end - - defp get_object_http(id) do + defp get_object(id) do date = Pleroma.Signature.signed_date() headers = diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index e388993b7..3aa6909d2 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -39,7 +39,7 @@ def key_id_to_actor_id(key_id) do def fetch_public_key(conn) do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), {:ok, actor_id} <- key_id_to_actor_id(kid), - {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id, force_http: true) do + {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} else e -> @@ -50,8 +50,8 @@ def fetch_public_key(conn) do def refetch_public_key(conn) do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), {:ok, actor_id} <- key_id_to_actor_id(kid), - {:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id, force_http: true), - {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id, force_http: true) do + {:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id), + {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} else e -> diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 66f5efca7..bcd5256c8 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1789,12 +1789,12 @@ def html_filter_policy(%User{no_rich_text: true}) do def html_filter_policy(_), do: Config.get([:markup, :scrub_policy]) - def fetch_by_ap_id(ap_id, opts \\ []), do: ActivityPub.make_user_from_ap_id(ap_id, opts) + def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id) - def get_or_fetch_by_ap_id(ap_id, opts \\ []) do + def get_or_fetch_by_ap_id(ap_id) do cached_user = get_cached_by_ap_id(ap_id) - maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id, opts) + maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id) case {cached_user, maybe_fetched_user} do {_, {:ok, %User{} = user}} -> @@ -1867,8 +1867,8 @@ def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do def public_key(_), do: {:error, "key not found"} - def get_public_key_for_ap_id(ap_id, opts \\ []) do - with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id, opts), + def get_public_key_for_ap_id(ap_id) do + with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), {:ok, public_key} <- public_key(user) do {:ok, public_key} else diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d8f685d38..35f71b7ae 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1289,12 +1289,10 @@ defp object_to_user_data(data) do def fetch_follow_information_for_user(user) do with {:ok, following_data} <- - Fetcher.fetch_and_contain_remote_object_from_id(user.following_address, - force_http: true - ), + Fetcher.fetch_and_contain_remote_object_from_id(user.following_address), {:ok, hide_follows} <- collection_private(following_data), {:ok, followers_data} <- - Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address, force_http: true), + Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address), {:ok, hide_followers} <- collection_private(followers_data) do {:ok, %{ @@ -1368,8 +1366,8 @@ def user_data_from_user_object(data) do end end - def fetch_and_prepare_user_from_ap_id(ap_id, opts \\ []) do - with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id, opts), + def fetch_and_prepare_user_from_ap_id(ap_id) do + with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id), {:ok, data} <- user_data_from_user_object(data) do {:ok, maybe_update_follow_information(data)} else @@ -1412,13 +1410,13 @@ def maybe_handle_clashing_nickname(data) do end end - def make_user_from_ap_id(ap_id, opts \\ []) do + def make_user_from_ap_id(ap_id) do user = User.get_cached_by_ap_id(ap_id) if user && !User.ap_enabled?(user) do Transmogrifier.upgrade_user_from_ap_id(ap_id) else - with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, opts) do + with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do if user do user |> User.remote_user_changeset(data) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index a2930c1cd..5ab3562bf 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -13,7 +13,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do alias Pleroma.User alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.FedSockets require Pleroma.Constants @@ -50,28 +49,6 @@ def is_representable?(%Activity{} = activity) do """ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do Logger.debug("Federating #{id} to #{inbox}") - - case FedSockets.get_or_create_fed_socket(inbox) do - {:ok, fedsocket} -> - Logger.debug("publishing via fedsockets - #{inspect(inbox)}") - FedSockets.publish(fedsocket, json) - - _ -> - Logger.debug("publishing via http - #{inspect(inbox)}") - http_publish(inbox, actor, json, params) - end - end - - def publish_one(%{actor_id: actor_id} = params) do - actor = User.get_cached_by_id(actor_id) - - params - |> Map.delete(:actor_id) - |> Map.put(:actor, actor) - |> publish_one() - end - - defp http_publish(inbox, actor, json, params) do uri = %{path: path} = URI.parse(inbox) digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) @@ -110,6 +87,15 @@ defp http_publish(inbox, actor, json, params) do end end + def publish_one(%{actor_id: actor_id} = params) do + actor = User.get_cached_by_id(actor_id) + + params + |> Map.delete(:actor_id) + |> Map.put(:actor, actor) + |> publish_one() + end + defp signature_host(%URI{port: port, scheme: scheme, host: host}) do if port == URI.default_port(scheme) do host diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 0bcd1db22..565d32433 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1008,7 +1008,7 @@ def perform(:user_upgrade, user) do def upgrade_user_from_ap_id(ap_id) do with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id), - {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id, force_http: true), + {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), {:ok, user} <- update_user(user, data) do TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) {:ok, user} diff --git a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex new file mode 100644 index 000000000..fac3522b8 --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.FrontendController do + use Pleroma.Web, :controller + + alias Pleroma.Config + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :install) + plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :index) + action_fallback(Pleroma.Web.AdminAPI.FallbackController) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.FrontendOperation + + def index(conn, _params) do + installed = installed() + + frontends = + [:frontends, :available] + |> Config.get([]) + |> Enum.map(fn {name, desc} -> + Map.put(desc, "installed", name in installed) + end) + + render(conn, "index.json", frontends: frontends) + end + + def install(%{body_params: params} = conn, _params) do + with :ok <- Pleroma.Frontend.install(params.name, Map.delete(params, :name)) do + index(conn, %{}) + end + end + + defp installed do + File.ls!(Pleroma.Frontend.dir()) + end +end diff --git a/lib/pleroma/web/admin_api/views/frontend_view.ex b/lib/pleroma/web/admin_api/views/frontend_view.ex new file mode 100644 index 000000000..374841d0b --- /dev/null +++ b/lib/pleroma/web/admin_api/views/frontend_view.ex @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.FrontendView do + use Pleroma.Web, :view + + def render("index.json", %{frontends: frontends}) do + render_many(frontends, __MODULE__, "show.json") + end + + def render("show.json", %{frontend: frontend}) do + %{ + name: frontend["name"], + git: frontend["git"], + build_url: frontend["build_url"], + ref: frontend["ref"], + installed: frontend["installed"] + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex new file mode 100644 index 000000000..96d4cdee7 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Admin", "Reports"], + summary: "Get a list of available frontends", + operationId: "AdminAPI.FrontendController.index", + security: [%{"oAuth" => ["read"]}], + responses: %{ + 200 => Operation.response("Response", "application/json", list_of_frontends()), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def install_operation do + %Operation{ + tags: ["Admin", "Reports"], + summary: "Install a frontend", + operationId: "AdminAPI.FrontendController.install", + security: [%{"oAuth" => ["read"]}], + requestBody: request_body("Parameters", install_request(), required: true), + responses: %{ + 200 => Operation.response("Response", "application/json", list_of_frontends()), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp list_of_frontends do + %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + name: %Schema{type: :string}, + git: %Schema{type: :string, format: :uri, nullable: true}, + build_url: %Schema{type: :string, format: :uri, nullable: true}, + ref: %Schema{type: :string}, + installed: %Schema{type: :boolean} + } + } + } + end + + defp install_request do + %Schema{ + title: "FrontendInstallRequest", + type: :object, + required: [:name], + properties: %{ + name: %Schema{ + type: :string + }, + ref: %Schema{ + type: :string + }, + file: %Schema{ + type: :string + }, + build_url: %Schema{ + type: :string + }, + build_dir: %Schema{ + type: :string + } + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/subscription_operation.ex b/lib/pleroma/web/api_spec/operations/subscription_operation.ex index 775dd795d..67c7ea8f3 100644 --- a/lib/pleroma/web/api_spec/operations/subscription_operation.ex +++ b/lib/pleroma/web/api_spec/operations/subscription_operation.ex @@ -146,6 +146,11 @@ defp create_request do allOf: [BooleanLike], nullable: true, description: "Receive chat notifications?" + }, + "pleroma:emoji_reaction": %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Receive emoji reaction notifications?" } } } @@ -210,6 +215,16 @@ defp update_request do allOf: [BooleanLike], nullable: true, description: "Receive poll notifications?" + }, + "pleroma:chat_mention": %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Receive chat notifications?" + }, + "pleroma:emoji_reaction": %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Receive emoji reaction notifications?" } } } diff --git a/lib/pleroma/web/fed_sockets.ex b/lib/pleroma/web/fed_sockets.ex deleted file mode 100644 index 1fd5899c8..000000000 --- a/lib/pleroma/web/fed_sockets.ex +++ /dev/null @@ -1,185 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets do - @moduledoc """ - This documents the FedSockets framework. A framework for federating - ActivityPub objects between servers via persistant WebSocket connections. - - FedSockets allow servers to authenticate on first contact and maintain that - connection, eliminating the need to authenticate every time data needs to be shared. - - ## Protocol - FedSockets currently support 2 types of data transfer: - * `publish` method which doesn't require a response - * `fetch` method requires a response be sent - - ### Publish - The publish operation sends a json encoded map of the shape: - %{action: :publish, data: json} - and accepts (but does not require) a reply of form: - %{"action" => "publish_reply"} - - The outgoing params represent - * data: ActivityPub object encoded into json - - - ### Fetch - The fetch operation sends a json encoded map of the shape: - %{action: :fetch, data: id, uuid: fetch_uuid} - and requires a reply of form: - %{"action" => "fetch_reply", "uuid" => uuid, "data" => data} - - The outgoing params represent - * id: an ActivityPub object URI - * uuid: a unique uuid generated by the sender - - The reply params represent - * data: an ActivityPub object encoded into json - * uuid: the uuid sent along with the fetch request - - ## Examples - Clients of FedSocket transfers shouldn't need to use any of the functions outside of this module. - - A typical publish operation can be performed through the following code, and a fetch operation in a similar manner. - - case FedSockets.get_or_create_fed_socket(inbox) do - {:ok, fedsocket} -> - FedSockets.publish(fedsocket, json) - - _ -> - alternative_publish(inbox, actor, json, params) - end - - ## Configuration - FedSockets have the following config settings - - config :pleroma, :fed_sockets, - enabled: true, - ping_interval: :timer.seconds(15), - connection_duration: :timer.hours(1), - rejection_duration: :timer.hours(1), - fed_socket_fetches: [ - default: 12_000, - interval: 3_000, - lazy: false - ] - * enabled - turn FedSockets on or off with this flag. Can be toggled at runtime. - * connection_duration - How long a FedSocket can sit idle before it's culled. - * rejection_duration - After failing to make a FedSocket connection a host will be excluded - from further connections for this amount of time - * fed_socket_fetches - Use these parameters to pass options to the Cachex queue backing the FetchRegistry - * fed_socket_rejections - Use these parameters to pass options to the Cachex queue backing the FedRegistry - - Cachex options are - * default: the minimum amount of time a fetch can wait before it times out. - * interval: the interval between checks for timed out entries. This plus the default represent the maximum time allowed - * lazy: leave at false for consistant and fast lookups, set to true for stricter timeout enforcement - - """ - require Logger - - alias Pleroma.Web.FedSockets.FedRegistry - alias Pleroma.Web.FedSockets.FedSocket - alias Pleroma.Web.FedSockets.SocketInfo - - @doc """ - returns a FedSocket for the given origin. Will reuse an existing one or create a new one. - - address is expected to be a fully formed URL such as: - "http://www.example.com" or "http://www.example.com:8080" - - It can and usually does include additional path parameters, - but these are ignored as the FedSockets are organized by host and port info alone. - """ - def get_or_create_fed_socket(address) do - with {:cache, {:error, :missing}} <- {:cache, get_fed_socket(address)}, - {:connect, {:ok, _pid}} <- {:connect, FedSocket.connect_to_host(address)}, - {:cache, {:ok, fed_socket}} <- {:cache, get_fed_socket(address)} do - Logger.debug("fedsocket created for - #{inspect(address)}") - {:ok, fed_socket} - else - {:cache, {:ok, socket}} -> - Logger.debug("fedsocket found in cache - #{inspect(address)}") - {:ok, socket} - - {:cache, {:error, :rejected} = e} -> - e - - {:connect, {:error, _host}} -> - Logger.debug("set host rejected for - #{inspect(address)}") - FedRegistry.set_host_rejected(address) - {:error, :rejected} - - {_, {:error, :disabled}} -> - {:error, :disabled} - - {_, {:error, reason}} -> - Logger.warn("get_or_create_fed_socket error - #{inspect(reason)}") - {:error, reason} - end - end - - @doc """ - returns a FedSocket for the given origin. Will not create a new FedSocket if one does not exist. - - address is expected to be a fully formed URL such as: - "http://www.example.com" or "http://www.example.com:8080" - """ - def get_fed_socket(address) do - origin = SocketInfo.origin(address) - - with {:config, true} <- {:config, Pleroma.Config.get([:fed_sockets, :enabled], false)}, - {:ok, socket} <- FedRegistry.get_fed_socket(origin) do - {:ok, socket} - else - {:config, _} -> - {:error, :disabled} - - {:error, :rejected} -> - Logger.debug("FedSocket previously rejected - #{inspect(origin)}") - {:error, :rejected} - - {:error, reason} -> - {:error, reason} - end - end - - @doc """ - Sends the supplied data via the publish protocol. - It will not block waiting for a reply. - Returns :ok but this is not an indication of a successful transfer. - - the data is expected to be JSON encoded binary data. - """ - def publish(%SocketInfo{} = fed_socket, json) do - FedSocket.publish(fed_socket, json) - end - - @doc """ - Sends the supplied data via the fetch protocol. - It will block waiting for a reply or timeout. - - Returns {:ok, object} where object is the requested object (or nil) - {:error, :timeout} in the event the message was not responded to - - the id is expected to be the URI of an ActivityPub object. - """ - def fetch(%SocketInfo{} = fed_socket, id) do - FedSocket.fetch(fed_socket, id) - end - - @doc """ - Disconnect all and restart FedSockets. - This is mainly used in development and testing but could be useful in production. - """ - def reset do - FedRegistry - |> Process.whereis() - |> Process.exit(:testing) - end - - def uri_for_origin(origin), - do: "ws://#{origin}/api/fedsocket/v1" -end diff --git a/lib/pleroma/web/fed_sockets/fed_registry.ex b/lib/pleroma/web/fed_sockets/fed_registry.ex deleted file mode 100644 index e00ea69c0..000000000 --- a/lib/pleroma/web/fed_sockets/fed_registry.ex +++ /dev/null @@ -1,185 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FedRegistry do - @moduledoc """ - The FedRegistry stores the active FedSockets for quick retrieval. - - The storage and retrieval portion of the FedRegistry is done in process through - elixir's `Registry` module for speed and its ability to monitor for terminated processes. - - Dropped connections will be caught by `Registry` and deleted. Since the next - message will initiate a new connection there is no reason to try and reconnect at that point. - - Normally outside modules should have no need to call or use the FedRegistry themselves. - """ - - alias Pleroma.Web.FedSockets.FedSocket - alias Pleroma.Web.FedSockets.SocketInfo - - require Logger - - @default_rejection_duration 15 * 60 * 1000 - @rejections :fed_socket_rejections - - @doc """ - Retrieves a FedSocket from the Registry given it's origin. - - The origin is expected to be a string identifying the endpoint "example.com" or "example2.com:8080" - - Will return: - * {:ok, fed_socket} for working FedSockets - * {:error, :rejected} for origins that have been tried and refused within the rejection duration interval - * {:error, some_reason} usually :missing for unknown origins - """ - def get_fed_socket(origin) do - case get_registry_data(origin) do - {:error, reason} -> - {:error, reason} - - {:ok, %{state: :connected} = socket_info} -> - {:ok, socket_info} - end - end - - @doc """ - Adds a connected FedSocket to the Registry. - - Always returns {:ok, fed_socket} - """ - def add_fed_socket(origin, pid \\ nil) do - origin - |> SocketInfo.build(pid) - |> SocketInfo.connect() - |> add_socket_info - end - - defp add_socket_info(%{origin: origin, state: :connected} = socket_info) do - case Registry.register(FedSockets.Registry, origin, socket_info) do - {:ok, _owner} -> - clear_prior_rejection(origin) - Logger.debug("fedsocket added: #{inspect(origin)}") - - {:ok, socket_info} - - {:error, {:already_registered, _pid}} -> - FedSocket.close(socket_info) - existing_socket_info = Registry.lookup(FedSockets.Registry, origin) - - {:ok, existing_socket_info} - - _ -> - {:error, :error_adding_socket} - end - end - - @doc """ - Mark this origin as having rejected a connection attempt. - This will keep it from getting additional connection attempts - for a period of time specified in the config. - - Always returns {:ok, new_reg_data} - """ - def set_host_rejected(uri) do - new_reg_data = - uri - |> SocketInfo.origin() - |> get_or_create_registry_data() - |> set_to_rejected() - |> save_registry_data() - - {:ok, new_reg_data} - end - - @doc """ - Retrieves the FedRegistryData from the Registry given it's origin. - - The origin is expected to be a string identifying the endpoint "example.com" or "example2.com:8080" - - Will return: - * {:ok, fed_registry_data} for known origins - * {:error, :missing} for uniknown origins - * {:error, :cache_error} indicating some low level runtime issues - """ - def get_registry_data(origin) do - case Registry.lookup(FedSockets.Registry, origin) do - [] -> - if is_rejected?(origin) do - Logger.debug("previously rejected fedsocket requested") - {:error, :rejected} - else - {:error, :missing} - end - - [{_pid, %{state: :connected} = socket_info}] -> - {:ok, socket_info} - - _ -> - {:error, :cache_error} - end - end - - @doc """ - Retrieves a map of all sockets from the Registry. The keys are the origins and the values are the corresponding SocketInfo - """ - def list_all do - (list_all_connected() ++ list_all_rejected()) - |> Enum.into(%{}) - end - - defp list_all_connected do - FedSockets.Registry - |> Registry.select([{{:"$1", :_, :"$3"}, [], [{{:"$1", :"$3"}}]}]) - end - - defp list_all_rejected do - {:ok, keys} = Cachex.keys(@rejections) - - {:ok, registry_data} = - Cachex.execute(@rejections, fn worker -> - Enum.map(keys, fn k -> {k, Cachex.get!(worker, k)} end) - end) - - registry_data - end - - defp clear_prior_rejection(origin), - do: Cachex.del(@rejections, origin) - - defp is_rejected?(origin) do - case Cachex.get(@rejections, origin) do - {:ok, nil} -> - false - - {:ok, _} -> - true - end - end - - defp get_or_create_registry_data(origin) do - case get_registry_data(origin) do - {:error, :missing} -> - %SocketInfo{origin: origin} - - {:ok, socket_info} -> - socket_info - end - end - - defp save_registry_data(%SocketInfo{origin: origin, state: :connected} = socket_info) do - {:ok, true} = Registry.update_value(FedSockets.Registry, origin, fn _ -> socket_info end) - socket_info - end - - defp save_registry_data(%SocketInfo{origin: origin, state: :rejected} = socket_info) do - rejection_expiration = - Pleroma.Config.get([:fed_sockets, :rejection_duration], @default_rejection_duration) - - {:ok, true} = Cachex.put(@rejections, origin, socket_info, ttl: rejection_expiration) - socket_info - end - - defp set_to_rejected(%SocketInfo{} = socket_info), - do: %SocketInfo{socket_info | state: :rejected} -end diff --git a/lib/pleroma/web/fed_sockets/fed_socket.ex b/lib/pleroma/web/fed_sockets/fed_socket.ex deleted file mode 100644 index 98d64e65a..000000000 --- a/lib/pleroma/web/fed_sockets/fed_socket.ex +++ /dev/null @@ -1,137 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FedSocket do - @moduledoc """ - The FedSocket module abstracts the actions to be taken taken on connections regardless of - whether the connection started as inbound or outbound. - - - Normally outside modules will have no need to call the FedSocket module directly. - """ - - alias Pleroma.Object - alias Pleroma.Object.Containment - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.FedSockets.FetchRegistry - alias Pleroma.Web.FedSockets.IngesterWorker - alias Pleroma.Web.FedSockets.OutgoingHandler - alias Pleroma.Web.FedSockets.SocketInfo - - require Logger - - @shake "61dd18f7-f1e6-49a4-939a-a749fcdc1103" - - def connect_to_host(uri) do - case OutgoingHandler.start_link(uri) do - {:ok, pid} -> - {:ok, pid} - - error -> - {:error, error} - end - end - - def close(%SocketInfo{pid: socket_pid}), - do: Process.send(socket_pid, :close, []) - - def publish(%SocketInfo{pid: socket_pid}, json) do - %{action: :publish, data: json} - |> Jason.encode!() - |> send_packet(socket_pid) - end - - def fetch(%SocketInfo{pid: socket_pid}, id) do - fetch_uuid = FetchRegistry.register_fetch(id) - - %{action: :fetch, data: id, uuid: fetch_uuid} - |> Jason.encode!() - |> send_packet(socket_pid) - - wait_for_fetch_to_return(fetch_uuid, 0) - end - - def receive_package(%SocketInfo{} = fed_socket, json) do - json - |> Jason.decode!() - |> process_package(fed_socket) - end - - defp wait_for_fetch_to_return(uuid, cntr) do - case FetchRegistry.check_fetch(uuid) do - {:error, :waiting} -> - Process.sleep(:math.pow(cntr, 3) |> Kernel.trunc()) - wait_for_fetch_to_return(uuid, cntr + 1) - - {:error, :missing} -> - Logger.error("FedSocket fetch timed out - #{inspect(uuid)}") - {:error, :timeout} - - {:ok, _fr} -> - FetchRegistry.pop_fetch(uuid) - end - end - - defp process_package(%{"action" => "publish", "data" => data}, %{origin: origin} = _fed_socket) do - if Containment.contain_origin(origin, data) do - IngesterWorker.enqueue("ingest", %{"object" => data}) - end - - {:reply, %{"action" => "publish_reply", "status" => "processed"}} - end - - defp process_package(%{"action" => "fetch_reply", "uuid" => uuid, "data" => data}, _fed_socket) do - FetchRegistry.register_fetch_received(uuid, data) - {:noreply, nil} - end - - defp process_package(%{"action" => "fetch", "uuid" => uuid, "data" => ap_id}, _fed_socket) do - {:ok, data} = render_fetched_data(ap_id, uuid) - {:reply, data} - end - - defp process_package(%{"action" => "publish_reply"}, _fed_socket) do - {:noreply, nil} - end - - defp process_package(other, _fed_socket) do - Logger.warn("unknown json packages received #{inspect(other)}") - {:noreply, nil} - end - - defp render_fetched_data(ap_id, uuid) do - {:ok, - %{ - "action" => "fetch_reply", - "status" => "processed", - "uuid" => uuid, - "data" => represent_item(ap_id) - }} - end - - defp represent_item(ap_id) do - case User.get_by_ap_id(ap_id) do - nil -> - object = Object.get_cached_by_ap_id(ap_id) - - if Visibility.is_public?(object) do - Phoenix.View.render_to_string(ObjectView, "object.json", object: object) - else - nil - end - - user -> - Phoenix.View.render_to_string(UserView, "user.json", user: user) - end - end - - defp send_packet(data, socket_pid) do - Process.send(socket_pid, {:send, data}, []) - end - - def shake, do: @shake -end diff --git a/lib/pleroma/web/fed_sockets/fetch_registry.ex b/lib/pleroma/web/fed_sockets/fetch_registry.ex deleted file mode 100644 index 7897f0fc6..000000000 --- a/lib/pleroma/web/fed_sockets/fetch_registry.ex +++ /dev/null @@ -1,151 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FetchRegistry do - @moduledoc """ - The FetchRegistry acts as a broker for fetch requests and return values. - This allows calling processes to block while waiting for a reply. - It doesn't impose it's own process instead using `Cachex` to handle fetches in process, allowing - multi threaded processes to avoid bottlenecking. - - Normally outside modules will have no need to call or use the FetchRegistry themselves. - - The `Cachex` parameters can be controlled from the config. Since exact timeout intervals - aren't necessary the following settings are used by default: - - config :pleroma, :fed_sockets, - fed_socket_fetches: [ - default: 12_000, - interval: 3_000, - lazy: false - ] - - """ - - defmodule FetchRegistryData do - defstruct uuid: nil, - sent_json: nil, - received_json: nil, - sent_at: nil, - received_at: nil - end - - alias Ecto.UUID - - require Logger - - @fetches :fed_socket_fetches - - @doc """ - Registers a json request wth the FetchRegistry and returns the identifying UUID. - """ - def register_fetch(json) do - %FetchRegistryData{uuid: uuid} = - json - |> new_registry_data - |> save_registry_data - - uuid - end - - @doc """ - Reports on the status of a Fetch given the identifying UUID. - - Will return - * {:ok, fetched_object} if a fetch has completed - * {:error, :waiting} if a fetch is still pending - * {:error, other_error} usually :missing to indicate a fetch that has timed out - """ - def check_fetch(uuid) do - case get_registry_data(uuid) do - {:ok, %FetchRegistryData{received_at: nil}} -> - {:error, :waiting} - - {:ok, %FetchRegistryData{} = reg_data} -> - {:ok, reg_data} - - e -> - e - end - end - - @doc """ - Retrieves the response to a fetch given the identifying UUID. - The completed fetch will be deleted from the FetchRegistry - - Will return - * {:ok, fetched_object} if a fetch has completed - * {:error, :waiting} if a fetch is still pending - * {:error, other_error} usually :missing to indicate a fetch that has timed out - """ - def pop_fetch(uuid) do - case check_fetch(uuid) do - {:ok, %FetchRegistryData{received_json: received_json}} -> - delete_registry_data(uuid) - {:ok, received_json} - - e -> - e - end - end - - @doc """ - This is called to register a fetch has returned. - It expects the result data along with the UUID that was sent in the request - - Will return the fetched object or :error - """ - def register_fetch_received(uuid, data) do - case get_registry_data(uuid) do - {:ok, %FetchRegistryData{received_at: nil} = reg_data} -> - reg_data - |> set_fetch_received(data) - |> save_registry_data() - - {:ok, %FetchRegistryData{} = reg_data} -> - Logger.warn("tried to add fetched data twice - #{uuid}") - reg_data - - {:error, _} -> - Logger.warn("Error adding fetch to registry - #{uuid}") - :error - end - end - - defp new_registry_data(json) do - %FetchRegistryData{ - uuid: UUID.generate(), - sent_json: json, - sent_at: :erlang.monotonic_time(:millisecond) - } - end - - defp get_registry_data(origin) do - case Cachex.get(@fetches, origin) do - {:ok, nil} -> - {:error, :missing} - - {:ok, reg_data} -> - {:ok, reg_data} - - _ -> - {:error, :cache_error} - end - end - - defp set_fetch_received(%FetchRegistryData{} = reg_data, data), - do: %FetchRegistryData{ - reg_data - | received_at: :erlang.monotonic_time(:millisecond), - received_json: data - } - - defp save_registry_data(%FetchRegistryData{uuid: uuid} = reg_data) do - {:ok, true} = Cachex.put(@fetches, uuid, reg_data) - reg_data - end - - defp delete_registry_data(origin), - do: {:ok, true} = Cachex.del(@fetches, origin) -end diff --git a/lib/pleroma/web/fed_sockets/incoming_handler.ex b/lib/pleroma/web/fed_sockets/incoming_handler.ex deleted file mode 100644 index 49d0d9d84..000000000 --- a/lib/pleroma/web/fed_sockets/incoming_handler.ex +++ /dev/null @@ -1,88 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.IncomingHandler do - require Logger - - alias Pleroma.Web.FedSockets.FedRegistry - alias Pleroma.Web.FedSockets.FedSocket - alias Pleroma.Web.FedSockets.SocketInfo - - import HTTPSignatures, only: [validate_conn: 1, split_signature: 1] - - @behaviour :cowboy_websocket - - def init(req, state) do - shake = FedSocket.shake() - - with true <- Pleroma.Config.get([:fed_sockets, :enabled]), - sec_protocol <- :cowboy_req.header("sec-websocket-protocol", req, nil), - headers = %{"(request-target)" => ^shake} <- :cowboy_req.headers(req), - true <- validate_conn(%{req_headers: headers}), - %{"keyId" => origin} <- split_signature(headers["signature"]) do - req = - if is_nil(sec_protocol) do - req - else - :cowboy_req.set_resp_header("sec-websocket-protocol", sec_protocol, req) - end - - {:cowboy_websocket, req, %{origin: origin}, %{}} - else - _ -> - {:ok, req, state} - end - end - - def websocket_init(%{origin: origin}) do - case FedRegistry.add_fed_socket(origin) do - {:ok, socket_info} -> - {:ok, socket_info} - - e -> - Logger.error("FedSocket websocket_init failed - #{inspect(e)}") - {:error, inspect(e)} - end - end - - # Use the ping to check if the connection should be expired - def websocket_handle(:ping, socket_info) do - if SocketInfo.expired?(socket_info) do - {:stop, socket_info} - else - {:ok, socket_info, :hibernate} - end - end - - def websocket_handle({:text, data}, socket_info) do - socket_info = SocketInfo.touch(socket_info) - - case FedSocket.receive_package(socket_info, data) do - {:noreply, _} -> - {:ok, socket_info} - - {:reply, reply} -> - {:reply, {:text, Jason.encode!(reply)}, socket_info} - - {:error, reason} -> - Logger.error("incoming error - receive_package: #{inspect(reason)}") - {:ok, socket_info} - end - end - - def websocket_info({:send, message}, socket_info) do - socket_info = SocketInfo.touch(socket_info) - - {:reply, {:text, message}, socket_info} - end - - def websocket_info(:close, state) do - {:stop, state} - end - - def websocket_info(message, state) do - Logger.debug("#{__MODULE__} unknown message #{inspect(message)}") - {:ok, state} - end -end diff --git a/lib/pleroma/web/fed_sockets/ingester_worker.ex b/lib/pleroma/web/fed_sockets/ingester_worker.ex deleted file mode 100644 index 325f2a4ab..000000000 --- a/lib/pleroma/web/fed_sockets/ingester_worker.ex +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.IngesterWorker do - use Pleroma.Workers.WorkerHelper, queue: "ingestion_queue" - require Logger - - alias Pleroma.Web.Federator - - @impl Oban.Worker - def perform(%Job{args: %{"op" => "ingest", "object" => ingestee}}) do - try do - ingestee - |> Jason.decode!() - |> do_ingestion() - rescue - e -> - Logger.error("IngesterWorker error - #{inspect(e)}") - e - end - end - - defp do_ingestion(params) do - case Federator.incoming_ap_doc(params) do - {:error, reason} -> - {:error, reason} - - {:ok, object} -> - {:ok, object} - end - end -end diff --git a/lib/pleroma/web/fed_sockets/outgoing_handler.ex b/lib/pleroma/web/fed_sockets/outgoing_handler.ex deleted file mode 100644 index e235a7c43..000000000 --- a/lib/pleroma/web/fed_sockets/outgoing_handler.ex +++ /dev/null @@ -1,151 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.OutgoingHandler do - use GenServer - - require Logger - - alias Pleroma.Application - alias Pleroma.Web.ActivityPub.InternalFetchActor - alias Pleroma.Web.FedSockets - alias Pleroma.Web.FedSockets.FedRegistry - alias Pleroma.Web.FedSockets.FedSocket - alias Pleroma.Web.FedSockets.SocketInfo - - def start_link(uri) do - GenServer.start_link(__MODULE__, %{uri: uri}) - end - - def init(%{uri: uri}) do - case initiate_connection(uri) do - {:ok, ws_origin, conn_pid} -> - FedRegistry.add_fed_socket(ws_origin, conn_pid) - - {:error, reason} -> - Logger.debug("Outgoing connection failed - #{inspect(reason)}") - :ignore - end - end - - def handle_info({:gun_ws, conn_pid, _ref, {:text, data}}, socket_info) do - socket_info = SocketInfo.touch(socket_info) - - case FedSocket.receive_package(socket_info, data) do - {:noreply, _} -> - {:noreply, socket_info} - - {:reply, reply} -> - :gun.ws_send(conn_pid, {:text, Jason.encode!(reply)}) - {:noreply, socket_info} - - {:error, reason} -> - Logger.error("incoming error - receive_package: #{inspect(reason)}") - {:noreply, socket_info} - end - end - - def handle_info(:close, state) do - Logger.debug("Sending close frame !!!!!!!") - {:close, state} - end - - def handle_info({:gun_down, _pid, _prot, :closed, _}, state) do - {:stop, :normal, state} - end - - def handle_info({:send, data}, %{conn_pid: conn_pid} = socket_info) do - socket_info = SocketInfo.touch(socket_info) - :gun.ws_send(conn_pid, {:text, data}) - {:noreply, socket_info} - end - - def handle_info({:gun_ws, _, _, :pong}, state) do - {:noreply, state, :hibernate} - end - - def handle_info(msg, state) do - Logger.debug("#{__MODULE__} unhandled event #{inspect(msg)}") - {:noreply, state} - end - - def terminate(reason, state) do - Logger.debug( - "#{__MODULE__} terminating outgoing connection for #{inspect(state)} for #{inspect(reason)}" - ) - - {:ok, state} - end - - def initiate_connection(uri) do - ws_uri = - uri - |> SocketInfo.origin() - |> FedSockets.uri_for_origin() - - %{host: host, port: port, path: path} = URI.parse(ws_uri) - - with {:ok, conn_pid} <- :gun.open(to_charlist(host), port, %{protocols: [:http]}), - {:ok, _} <- :gun.await_up(conn_pid), - reference <- - :gun.get(conn_pid, to_charlist(path), [ - {'user-agent', to_charlist(Application.user_agent())} - ]), - {:response, :fin, 204, _} <- :gun.await(conn_pid, reference), - headers <- build_headers(uri), - ref <- :gun.ws_upgrade(conn_pid, to_charlist(path), headers, %{silence_pings: false}) do - receive do - {:gun_upgrade, ^conn_pid, ^ref, [<<"websocket">>], _} -> - {:ok, ws_uri, conn_pid} - after - 15_000 -> - Logger.debug("Fedsocket timeout connecting to #{inspect(uri)}") - {:error, :timeout} - end - else - {:response, :nofin, 404, _} -> - {:error, :fedsockets_not_supported} - - e -> - Logger.debug("Fedsocket error connecting to #{inspect(uri)}") - {:error, e} - end - end - - defp build_headers(uri) do - host_for_sig = uri |> URI.parse() |> host_signature() - - shake = FedSocket.shake() - digest = "SHA-256=" <> (:crypto.hash(:sha256, shake) |> Base.encode64()) - date = Pleroma.Signature.signed_date() - shake_size = byte_size(shake) - - signature_opts = %{ - "(request-target)": shake, - "content-length": to_charlist("#{shake_size}"), - date: date, - digest: digest, - host: host_for_sig - } - - signature = Pleroma.Signature.sign(InternalFetchActor.get_actor(), signature_opts) - - [ - {'signature', to_charlist(signature)}, - {'date', date}, - {'digest', to_charlist(digest)}, - {'content-length', to_charlist("#{shake_size}")}, - {to_charlist("(request-target)"), to_charlist(shake)}, - {'user-agent', to_charlist(Application.user_agent())} - ] - end - - defp host_signature(%{host: host, scheme: scheme, port: port}) do - if port == URI.default_port(scheme) do - host - else - "#{host}:#{port}" - end - end -end diff --git a/lib/pleroma/web/fed_sockets/socket_info.ex b/lib/pleroma/web/fed_sockets/socket_info.ex deleted file mode 100644 index d6fdffe1a..000000000 --- a/lib/pleroma/web/fed_sockets/socket_info.ex +++ /dev/null @@ -1,52 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.SocketInfo do - defstruct origin: nil, - pid: nil, - conn_pid: nil, - state: :default, - connected_until: nil - - alias Pleroma.Web.FedSockets.SocketInfo - @default_connection_duration 15 * 60 * 1000 - - def build(uri, conn_pid \\ nil) do - uri - |> build_origin() - |> build_pids(conn_pid) - |> touch() - end - - def touch(%SocketInfo{} = socket_info), - do: %{socket_info | connected_until: new_ttl()} - - def connect(%SocketInfo{} = socket_info), - do: %{socket_info | state: :connected} - - def expired?(%{connected_until: connected_until}), - do: connected_until < :erlang.monotonic_time(:millisecond) - - def origin(uri), - do: build_origin(uri).origin - - defp build_pids(socket_info, conn_pid), - do: struct(socket_info, pid: self(), conn_pid: conn_pid) - - defp build_origin(uri) when is_binary(uri), - do: uri |> URI.parse() |> build_origin - - defp build_origin(%{host: host, port: nil, scheme: scheme}), - do: build_origin(%{host: host, port: URI.default_port(scheme)}) - - defp build_origin(%{host: host, port: port}), - do: %SocketInfo{origin: "#{host}:#{port}"} - - defp new_ttl do - connection_duration = - Pleroma.Config.get([:fed_sockets, :connection_duration], @default_connection_duration) - - :erlang.monotonic_time(:millisecond) + connection_duration - end -end diff --git a/lib/pleroma/web/fed_sockets/supervisor.ex b/lib/pleroma/web/fed_sockets/supervisor.ex deleted file mode 100644 index a5f4bebfb..000000000 --- a/lib/pleroma/web/fed_sockets/supervisor.ex +++ /dev/null @@ -1,59 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.Supervisor do - use Supervisor - import Cachex.Spec - - def start_link(opts) do - Supervisor.start_link(__MODULE__, opts, name: __MODULE__) - end - - def init(args) do - children = [ - build_cache(:fed_socket_fetches, args), - build_cache(:fed_socket_rejections, args), - {Registry, keys: :unique, name: FedSockets.Registry, meta: [rejected: %{}]} - ] - - opts = [strategy: :one_for_all, name: Pleroma.Web.Streamer.Supervisor] - Supervisor.init(children, opts) - end - - defp build_cache(name, args) do - opts = get_opts(name, args) - - %{ - id: String.to_atom("#{name}_cache"), - start: {Cachex, :start_link, [name, opts]}, - type: :worker - } - end - - defp get_opts(cache_name, args) - when cache_name in [:fed_socket_fetches, :fed_socket_rejections] do - default = get_opts_or_config(args, cache_name, :default, 15_000) - interval = get_opts_or_config(args, cache_name, :interval, 3_000) - lazy = get_opts_or_config(args, cache_name, :lazy, false) - - [expiration: expiration(default: default, interval: interval, lazy: lazy)] - end - - defp get_opts(name, args) do - Keyword.get(args, name, []) - end - - defp get_opts_or_config(args, name, key, default) do - args - |> Keyword.get(name, []) - |> Keyword.get(key) - |> case do - nil -> - Pleroma.Config.get([:fed_sockets, name, key], default) - - value -> - value - end - end -end diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index da535aa68..82152dffa 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query - @types ["Create", "Follow", "Announce", "Like", "Move"] + @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"] @doc "Performs sending notifications for user subscriptions" @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} @@ -149,6 +149,15 @@ def format_body( "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end + def format_body( + %{activity: %{data: %{"type" => "EmojiReact", "content" => content}}}, + actor, + _object, + _mastodon_type + ) do + "@#{actor.nickname} reacted with #{content}" + end + def format_body( %{activity: %{data: %{"type" => type}}} = notification, actor, @@ -179,6 +188,7 @@ def format_title(%{type: type}, mastodon_type) do "reblog" -> "New Repeat" "favourite" -> "New Favorite" "pleroma:chat_mention" -> "New Chat Message" + "pleroma:emoji_reaction" -> "New Reaction" type -> "New #{String.capitalize(type || "event")}" end end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 5b5aa0d59..749a573ba 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -25,7 +25,8 @@ defmodule Pleroma.Web.Push.Subscription do timestamps() end - @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention]a + # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength + @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a defp alerts(%{data: %{alerts: alerts}}) do alerts = Map.take(alerts, @supported_alert_types) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0f0538182..75a885377 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -244,6 +244,9 @@ defmodule Pleroma.Web.Router do get("/chats/:id/messages", ChatController, :messages) delete("/chats/:id/messages/:message_id", ChatController, :delete_message) + get("/frontends", FrontendController, :index) + post("/frontends/install", FrontendController, :install) + post("/backups", AdminAPIController, :create_backup) end diff --git a/priv/static/adminfe/chunk-03c5.f59788cf.css b/priv/static/adminfe/chunk-03c5.e6a0e2d0.css similarity index 100% rename from priv/static/adminfe/chunk-03c5.f59788cf.css rename to priv/static/adminfe/chunk-03c5.e6a0e2d0.css diff --git a/priv/static/adminfe/chunk-9d55.e2cb1409.css b/priv/static/adminfe/chunk-0492.15b0611f.css similarity index 100% rename from priv/static/adminfe/chunk-9d55.e2cb1409.css rename to priv/static/adminfe/chunk-0492.15b0611f.css diff --git a/priv/static/adminfe/chunk-342d.e342722b.css b/priv/static/adminfe/chunk-04b0.7e25cd78.css similarity index 97% rename from priv/static/adminfe/chunk-342d.e342722b.css rename to priv/static/adminfe/chunk-04b0.7e25cd78.css index b0fd8dcb3..8dfdc0dcf 100644 Binary files a/priv/static/adminfe/chunk-342d.e342722b.css and b/priv/static/adminfe/chunk-04b0.7e25cd78.css differ diff --git a/priv/static/adminfe/chunk-546f.692d1ab2.css b/priv/static/adminfe/chunk-0537.cd83e5d6.css similarity index 100% rename from priv/static/adminfe/chunk-546f.692d1ab2.css rename to priv/static/adminfe/chunk-0537.cd83e5d6.css diff --git a/priv/static/adminfe/chunk-170f.fea927c5.css b/priv/static/adminfe/chunk-170f.fea927c5.css new file mode 100644 index 000000000..a4ab52d51 Binary files /dev/null and b/priv/static/adminfe/chunk-170f.fea927c5.css differ diff --git a/priv/static/adminfe/chunk-176e.a3c8376d.css b/priv/static/adminfe/chunk-176e.d9a630b2.css similarity index 100% rename from priv/static/adminfe/chunk-176e.a3c8376d.css rename to priv/static/adminfe/chunk-176e.d9a630b2.css diff --git a/priv/static/adminfe/chunk-521f.b745ee5d.css b/priv/static/adminfe/chunk-1944.731ba892.css similarity index 62% rename from priv/static/adminfe/chunk-521f.b745ee5d.css rename to priv/static/adminfe/chunk-1944.731ba892.css index 7e8ffb651..6392d8e75 100644 Binary files a/priv/static/adminfe/chunk-521f.b745ee5d.css and b/priv/static/adminfe/chunk-1944.731ba892.css differ diff --git a/priv/static/adminfe/chunk-654d.94689c39.css b/priv/static/adminfe/chunk-654d.94689c39.css deleted file mode 100644 index 483d88545..000000000 Binary files a/priv/static/adminfe/chunk-654d.94689c39.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-68ea9.dac85813.css b/priv/static/adminfe/chunk-68ea9.8331e95e.css similarity index 100% rename from priv/static/adminfe/chunk-68ea9.dac85813.css rename to priv/static/adminfe/chunk-68ea9.8331e95e.css diff --git a/priv/static/adminfe/chunk-6e81.1c0f2da2.css b/priv/static/adminfe/chunk-6e81.559b76f9.css similarity index 100% rename from priv/static/adminfe/chunk-6e81.1c0f2da2.css rename to priv/static/adminfe/chunk-6e81.559b76f9.css diff --git a/priv/static/adminfe/chunk-7968.283bc086.css b/priv/static/adminfe/chunk-7968.283bc086.css new file mode 100644 index 000000000..5d9863d3a Binary files /dev/null and b/priv/static/adminfe/chunk-7968.283bc086.css differ diff --git a/priv/static/adminfe/chunk-7c6b.365cbeda.css b/priv/static/adminfe/chunk-7c6b.b633878a.css similarity index 100% rename from priv/static/adminfe/chunk-7c6b.365cbeda.css rename to priv/static/adminfe/chunk-7c6b.b633878a.css diff --git a/priv/static/adminfe/chunk-850d.cc4f0ac6.css b/priv/static/adminfe/chunk-850d.cc4f0ac6.css deleted file mode 100644 index 1cb2ead63..000000000 Binary files a/priv/static/adminfe/chunk-850d.cc4f0ac6.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-8fbb.dd321643.css b/priv/static/adminfe/chunk-8fbb.dd321643.css new file mode 100644 index 000000000..f50d974bd Binary files /dev/null and b/priv/static/adminfe/chunk-8fbb.dd321643.css differ diff --git a/priv/static/adminfe/chunk-3365.201aa8e6.css b/priv/static/adminfe/chunk-ad1e.1a3c5339.css similarity index 100% rename from priv/static/adminfe/chunk-3365.201aa8e6.css rename to priv/static/adminfe/chunk-ad1e.1a3c5339.css diff --git a/priv/static/adminfe/chunk-commons.c0eb3eb7.css b/priv/static/adminfe/chunk-commons.f7c3d390.css similarity index 100% rename from priv/static/adminfe/chunk-commons.c0eb3eb7.css rename to priv/static/adminfe/chunk-commons.f7c3d390.css diff --git a/priv/static/adminfe/chunk-d34d.b0dd6fb4.css b/priv/static/adminfe/chunk-e660.9e75af5b.css similarity index 100% rename from priv/static/adminfe/chunk-d34d.b0dd6fb4.css rename to priv/static/adminfe/chunk-e660.9e75af5b.css diff --git a/priv/static/adminfe/chunk-f364.6b5f3f0d.css b/priv/static/adminfe/chunk-f364.6b5f3f0d.css new file mode 100644 index 000000000..ec665da84 Binary files /dev/null and b/priv/static/adminfe/chunk-f364.6b5f3f0d.css differ diff --git a/priv/static/adminfe/chunk-f625.25a6a4ae.css b/priv/static/adminfe/chunk-f625.bcd0ea3b.css similarity index 100% rename from priv/static/adminfe/chunk-f625.25a6a4ae.css rename to priv/static/adminfe/chunk-f625.bcd0ea3b.css diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index d6b9b22b8..e6af40e97 100644 --- a/priv/static/adminfe/index.html +++ b/priv/static/adminfe/index.html @@ -1 +1 @@ -Admin FE
\ No newline at end of file +Admin FE
\ No newline at end of file diff --git a/priv/static/adminfe/static/js/app.69891fda.js b/priv/static/adminfe/static/js/app.69891fda.js deleted file mode 100644 index 3d04d9273..000000000 Binary files a/priv/static/adminfe/static/js/app.69891fda.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.69891fda.js.map b/priv/static/adminfe/static/js/app.69891fda.js.map deleted file mode 100644 index 0131793e9..000000000 Binary files a/priv/static/adminfe/static/js/app.69891fda.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.c67f9a2f.js b/priv/static/adminfe/static/js/app.c67f9a2f.js new file mode 100644 index 000000000..65f9d4a29 Binary files /dev/null and b/priv/static/adminfe/static/js/app.c67f9a2f.js differ diff --git a/priv/static/adminfe/static/js/app.c67f9a2f.js.map b/priv/static/adminfe/static/js/app.c67f9a2f.js.map new file mode 100644 index 000000000..41b4375aa Binary files /dev/null and b/priv/static/adminfe/static/js/app.c67f9a2f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js b/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-03c5.1c694c49.js rename to priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js index b4601abae..a89c65572 100644 Binary files a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js and b/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map b/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map rename to priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map index 193c65bb1..963ff6dee 100644 Binary files a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map and b/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js new file mode 100644 index 000000000..243ecde70 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js differ diff --git a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map new file mode 100644 index 000000000..f5e0d9ebc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js new file mode 100644 index 000000000..9d0352814 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js differ diff --git a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map new file mode 100644 index 000000000..a9bee3721 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-546f.81668ba7.js rename to priv/static/adminfe/static/js/chunk-0537.74db16b0.js index 252991021..35231e562 100644 Binary files a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js and b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js differ diff --git a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map rename to priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map index 6a9d2a8dc..fa87bd76d 100644 Binary files a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map and b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js b/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js new file mode 100644 index 000000000..d40dc29bd Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js differ diff --git a/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js.map b/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js.map new file mode 100644 index 000000000..91d3bc70d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-176e.be050aba.js b/priv/static/adminfe/static/js/chunk-176e.f64cb745.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-176e.be050aba.js rename to priv/static/adminfe/static/js/chunk-176e.f64cb745.js index cab83489b..dd60693da 100644 Binary files a/priv/static/adminfe/static/js/chunk-176e.be050aba.js and b/priv/static/adminfe/static/js/chunk-176e.f64cb745.js differ diff --git a/priv/static/adminfe/static/js/chunk-176e.be050aba.js.map b/priv/static/adminfe/static/js/chunk-176e.f64cb745.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-176e.be050aba.js.map rename to priv/static/adminfe/static/js/chunk-176e.f64cb745.js.map index bff959cd6..9ee6aa6e4 100644 Binary files a/priv/static/adminfe/static/js/chunk-176e.be050aba.js.map and b/priv/static/adminfe/static/js/chunk-176e.f64cb745.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js new file mode 100644 index 000000000..87590c6ce Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js differ diff --git a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map new file mode 100644 index 000000000..23229293e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js deleted file mode 100644 index 5ee311c4a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map deleted file mode 100644 index b73bbb0aa..000000000 Binary files a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-521f.748b331d.js b/priv/static/adminfe/static/js/chunk-521f.748b331d.js deleted file mode 100644 index 570dab224..000000000 Binary files a/priv/static/adminfe/static/js/chunk-521f.748b331d.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map b/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map deleted file mode 100644 index 3380bbbd5..000000000 Binary files a/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-654d.653b067f.js b/priv/static/adminfe/static/js/chunk-654d.653b067f.js deleted file mode 100644 index 209873ec1..000000000 Binary files a/priv/static/adminfe/static/js/chunk-654d.653b067f.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map b/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map deleted file mode 100644 index 72aca0e98..000000000 Binary files a/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js b/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js rename to priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js index 02091ed84..60056454d 100644 Binary files a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js and b/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map b/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map rename to priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map index 019cede66..9e26519c3 100644 Binary files a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map and b/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js b/priv/static/adminfe/static/js/chunk-6e81.afade883.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js rename to priv/static/adminfe/static/js/chunk-6e81.afade883.js index cd79db1d3..3b5dd6c5c 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js and b/priv/static/adminfe/static/js/chunk-6e81.afade883.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map b/priv/static/adminfe/static/js/chunk-6e81.afade883.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map rename to priv/static/adminfe/static/js/chunk-6e81.afade883.js.map index 10b437760..a0f7fca19 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map and b/priv/static/adminfe/static/js/chunk-6e81.afade883.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js new file mode 100644 index 000000000..dc981706f Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js differ diff --git a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map new file mode 100644 index 000000000..c2f0726b7 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js b/priv/static/adminfe/static/js/chunk-7c6b.34152862.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-7c6b.56a14571.js rename to priv/static/adminfe/static/js/chunk-7c6b.34152862.js index df9b7d6ac..27d57d3ff 100644 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js and b/priv/static/adminfe/static/js/chunk-7c6b.34152862.js differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map b/priv/static/adminfe/static/js/chunk-7c6b.34152862.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map rename to priv/static/adminfe/static/js/chunk-7c6b.34152862.js.map index 6584ba082..78026f5f4 100644 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map and b/priv/static/adminfe/static/js/chunk-7c6b.34152862.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js deleted file mode 100644 index a2c2df2e7..000000000 Binary files a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map deleted file mode 100644 index 7f7718547..000000000 Binary files a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js new file mode 100644 index 000000000..74ffe9194 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js differ diff --git a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map new file mode 100644 index 000000000..b3c3b5fe8 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js deleted file mode 100644 index 89d35af79..000000000 Binary files a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map deleted file mode 100644 index fa8694b8e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js b/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-3365.b73c30a8.js rename to priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js index 421bf2a99..82ddd4df2 100644 Binary files a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js and b/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js differ diff --git a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map b/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map rename to priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map index d2ad4d9aa..d74c2498f 100644 Binary files a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map and b/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-commons.a6002038.js b/priv/static/adminfe/static/js/chunk-commons.4ae74caa.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-commons.a6002038.js rename to priv/static/adminfe/static/js/chunk-commons.4ae74caa.js index 2b16da9c7..1ee2ea9e4 100644 Binary files a/priv/static/adminfe/static/js/chunk-commons.a6002038.js and b/priv/static/adminfe/static/js/chunk-commons.4ae74caa.js differ diff --git a/priv/static/adminfe/static/js/chunk-commons.a6002038.js.map b/priv/static/adminfe/static/js/chunk-commons.4ae74caa.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-commons.a6002038.js.map rename to priv/static/adminfe/static/js/chunk-commons.4ae74caa.js.map index 3c7d78861..41a884d15 100644 Binary files a/priv/static/adminfe/static/js/chunk-commons.a6002038.js.map and b/priv/static/adminfe/static/js/chunk-commons.4ae74caa.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js b/priv/static/adminfe/static/js/chunk-e660.feca27c4.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js rename to priv/static/adminfe/static/js/chunk-e660.feca27c4.js index edd221e6e..5659d263e 100644 Binary files a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js and b/priv/static/adminfe/static/js/chunk-e660.feca27c4.js differ diff --git a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map b/priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map rename to priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map index 6bcd4ed8b..cfc2e08af 100644 Binary files a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map and b/priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-f364.1122502b.js b/priv/static/adminfe/static/js/chunk-f364.1122502b.js new file mode 100644 index 000000000..facad2ed5 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f364.1122502b.js differ diff --git a/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map b/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map new file mode 100644 index 000000000..f89dabe30 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-f625.29237434.js b/priv/static/adminfe/static/js/chunk-f625.904137fd.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-f625.29237434.js rename to priv/static/adminfe/static/js/chunk-f625.904137fd.js index 522755a98..053590b28 100644 Binary files a/priv/static/adminfe/static/js/chunk-f625.29237434.js and b/priv/static/adminfe/static/js/chunk-f625.904137fd.js differ diff --git a/priv/static/adminfe/static/js/chunk-f625.29237434.js.map b/priv/static/adminfe/static/js/chunk-f625.904137fd.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-f625.29237434.js.map rename to priv/static/adminfe/static/js/chunk-f625.904137fd.js.map index 4f8774c3a..59c1c274e 100644 Binary files a/priv/static/adminfe/static/js/chunk-f625.29237434.js.map and b/priv/static/adminfe/static/js/chunk-f625.904137fd.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.8f631d12.js b/priv/static/adminfe/static/js/runtime.8f631d12.js deleted file mode 100644 index 6fa7d9ee1..000000000 Binary files a/priv/static/adminfe/static/js/runtime.8f631d12.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.8f631d12.js.map b/priv/static/adminfe/static/js/runtime.8f631d12.js.map deleted file mode 100644 index d5c20400e..000000000 Binary files a/priv/static/adminfe/static/js/runtime.8f631d12.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.ba96836e.js b/priv/static/adminfe/static/js/runtime.ba96836e.js new file mode 100644 index 000000000..245c7fe20 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.ba96836e.js differ diff --git a/priv/static/adminfe/static/js/runtime.ba96836e.js.map b/priv/static/adminfe/static/js/runtime.ba96836e.js.map new file mode 100644 index 000000000..f3c5a82af Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.ba96836e.js.map differ diff --git a/test/pleroma/frontend_test.exs b/test/pleroma/frontend_test.exs new file mode 100644 index 000000000..223625857 --- /dev/null +++ b/test/pleroma/frontend_test.exs @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FrontendTest do + use Pleroma.DataCase + alias Pleroma.Frontend + + @dir "test/frontend_static_test" + + setup do + File.mkdir_p!(@dir) + clear_config([:instance, :static_dir], @dir) + + on_exit(fn -> + File.rm_rf(@dir) + end) + end + + test "it downloads and unzips a known frontend" do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_url" => "http://gensokyo.2hu/builds/${ref}" + } + }) + + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")} + end) + + Frontend.install("pleroma") + + assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) + end + + test "it also works given a file" do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_dir" => "" + } + }) + + folder = Path.join([@dir, "frontends", "pleroma", "fantasy"]) + previously_existing = Path.join([folder, "temp"]) + File.mkdir_p!(folder) + File.write!(previously_existing, "yey") + assert File.exists?(previously_existing) + + Frontend.install("pleroma", file: "test/fixtures/tesla_mock/frontend.zip") + + assert File.exists?(Path.join([folder, "test.txt"])) + refute File.exists?(previously_existing) + end + + test "it downloads and unzips unknown frontends" do + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")} + end) + + Frontend.install("unknown", + ref: "baka", + build_url: "http://gensokyo.2hu/madeup.zip", + build_dir: "" + ) + + assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"])) + end +end diff --git a/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs new file mode 100644 index 000000000..94873f6db --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs @@ -0,0 +1,141 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Config + + @dir "test/frontend_static_test" + + setup do + clear_config([:instance, :static_dir], @dir) + File.mkdir_p!(Pleroma.Frontend.dir()) + + on_exit(fn -> + File.rm_rf(@dir) + end) + + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/frontends" do + test "it lists available frontends", %{conn: conn} do + response = + conn + |> get("/api/pleroma/admin/frontends") + |> json_response_and_validate_schema(:ok) + + assert Enum.map(response, & &1["name"]) == + Enum.map(Config.get([:frontends, :available]), fn {_, map} -> map["name"] end) + + refute Enum.any?(response, fn frontend -> frontend["installed"] == true end) + end + end + + describe "POST /api/pleroma/admin/frontends/install" do + test "from available frontends", %{conn: conn} do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_url" => "http://gensokyo.2hu/builds/${ref}" + } + }) + + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")} + end) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/frontends/install", %{name: "pleroma"}) + |> json_response_and_validate_schema(:ok) + + assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) + + response = + conn + |> get("/api/pleroma/admin/frontends") + |> json_response_and_validate_schema(:ok) + + assert response == [ + %{ + "build_url" => "http://gensokyo.2hu/builds/${ref}", + "git" => nil, + "installed" => true, + "name" => "pleroma", + "ref" => "fantasy" + } + ] + end + + test "from a file", %{conn: conn} do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_dir" => "" + } + }) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/frontends/install", %{ + name: "pleroma", + file: "test/fixtures/tesla_mock/frontend.zip" + }) + |> json_response_and_validate_schema(:ok) + + assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) + end + + test "from an URL", %{conn: conn} do + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")} + end) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/frontends/install", %{ + name: "unknown", + ref: "baka", + build_url: "http://gensokyo.2hu/madeup.zip", + build_dir: "" + }) + |> json_response_and_validate_schema(:ok) + + assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"])) + end + + test "failing returns an error", %{conn: conn} do + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} -> + %Tesla.Env{status: 404, body: ""} + end) + + result = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/frontends/install", %{ + name: "unknown", + ref: "baka", + build_url: "http://gensokyo.2hu/madeup.zip", + build_dir: "" + }) + |> json_response_and_validate_schema(400) + + assert result == %{"error" => "Could not download or unzip the frontend"} + end + end +end diff --git a/test/pleroma/web/fed_sockets/fed_registry_test.exs b/test/pleroma/web/fed_sockets/fed_registry_test.exs deleted file mode 100644 index 73aaced46..000000000 --- a/test/pleroma/web/fed_sockets/fed_registry_test.exs +++ /dev/null @@ -1,124 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FedRegistryTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets - alias Pleroma.Web.FedSockets.FedRegistry - alias Pleroma.Web.FedSockets.SocketInfo - - @good_domain "http://good.domain" - @good_domain_origin "good.domain:80" - - setup do - start_supervised({Pleroma.Web.FedSockets.Supervisor, []}) - build_test_socket(@good_domain) - Process.sleep(10) - - :ok - end - - describe "add_fed_socket/1 without conflicting sockets" do - test "can be added" do - Process.sleep(10) - assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) - assert origin == "good.domain:80" - end - - test "multiple origins can be added" do - build_test_socket("http://anothergood.domain") - Process.sleep(10) - - assert {:ok, %SocketInfo{origin: origin_1}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert {:ok, %SocketInfo{origin: origin_2}} = - FedRegistry.get_fed_socket("anothergood.domain:80") - - assert origin_1 == "good.domain:80" - assert origin_2 == "anothergood.domain:80" - assert FedRegistry.list_all() |> Enum.count() == 2 - end - end - - describe "add_fed_socket/1 when duplicate sockets conflict" do - setup do - build_test_socket(@good_domain) - build_test_socket(@good_domain) - Process.sleep(10) - :ok - end - - test "will be ignored" do - assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - - assert FedRegistry.list_all() |> Enum.count() == 1 - end - - test "the newer process will be closed" do - pid_two = build_test_socket(@good_domain) - - assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - Process.sleep(10) - - refute Process.alive?(pid_two) - - assert FedRegistry.list_all() |> Enum.count() == 1 - end - end - - describe "get_fed_socket/1" do - test "returns missing for unknown hosts" do - assert {:error, :missing} = FedRegistry.get_fed_socket("not_a_dmoain") - end - - test "returns rejected for hosts previously rejected" do - "rejected.domain:80" - |> FedSockets.uri_for_origin() - |> FedRegistry.set_host_rejected() - - assert {:error, :rejected} = FedRegistry.get_fed_socket("rejected.domain:80") - end - - test "can retrieve a previously added SocketInfo" do - build_test_socket(@good_domain) - Process.sleep(10) - assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) - assert origin == "good.domain:80" - end - - test "removes references to SocketInfos when the process crashes" do - assert {:ok, %SocketInfo{origin: origin, pid: pid}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - - Process.exit(pid, :testing) - Process.sleep(100) - assert {:error, :missing} = FedRegistry.get_fed_socket(@good_domain_origin) - end - end - - def build_test_socket(uri) do - Kernel.spawn(fn -> fed_socket_almost(uri) end) - end - - def fed_socket_almost(origin) do - FedRegistry.add_fed_socket(origin) - - receive do - :close -> - :ok - after - 5_000 -> :timeout - end - end -end diff --git a/test/pleroma/web/fed_sockets/fetch_registry_test.exs b/test/pleroma/web/fed_sockets/fetch_registry_test.exs deleted file mode 100644 index 7bd2d995a..000000000 --- a/test/pleroma/web/fed_sockets/fetch_registry_test.exs +++ /dev/null @@ -1,67 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FetchRegistryTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets.FetchRegistry - alias Pleroma.Web.FedSockets.FetchRegistry.FetchRegistryData - - @json_message "hello" - @json_reply "hello back" - - setup do - start_supervised( - {Pleroma.Web.FedSockets.Supervisor, - [ - ping_interval: 8, - connection_duration: 15, - rejection_duration: 5, - fed_socket_fetches: [default: 10, interval: 10] - ]} - ) - - :ok - end - - test "fetches can be stored" do - uuid = FetchRegistry.register_fetch(@json_message) - - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - end - - test "fetches can return" do - uuid = FetchRegistry.register_fetch(@json_message) - task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) - - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - Task.await(task) - - assert {:ok, %FetchRegistryData{received_json: received_json}} = - FetchRegistry.check_fetch(uuid) - - assert received_json == @json_reply - end - - test "fetches are deleted once popped from stack" do - uuid = FetchRegistry.register_fetch(@json_message) - task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) - Task.await(task) - - assert {:ok, %FetchRegistryData{received_json: received_json}} = - FetchRegistry.check_fetch(uuid) - - assert received_json == @json_reply - assert {:ok, @json_reply} = FetchRegistry.pop_fetch(uuid) - - assert {:error, :missing} = FetchRegistry.check_fetch(uuid) - end - - test "fetches can time out" do - uuid = FetchRegistry.register_fetch(@json_message) - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - Process.sleep(500) - assert {:error, :missing} = FetchRegistry.check_fetch(uuid) - end -end diff --git a/test/pleroma/web/fed_sockets/socket_info_test.exs b/test/pleroma/web/fed_sockets/socket_info_test.exs deleted file mode 100644 index db3d6edcd..000000000 --- a/test/pleroma/web/fed_sockets/socket_info_test.exs +++ /dev/null @@ -1,118 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.SocketInfoTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets - alias Pleroma.Web.FedSockets.SocketInfo - - describe "uri_for_origin" do - test "provides the fed_socket URL given the origin information" do - endpoint = "example.com:4000" - assert FedSockets.uri_for_origin(endpoint) =~ "ws://" - assert FedSockets.uri_for_origin(endpoint) =~ endpoint - end - end - - describe "origin" do - test "will provide the origin field given a url" do - endpoint = "example.com:4000" - assert SocketInfo.origin("ws://#{endpoint}") == endpoint - assert SocketInfo.origin("http://#{endpoint}") == endpoint - assert SocketInfo.origin("https://#{endpoint}") == endpoint - end - - test "will proide the origin field given a uri" do - endpoint = "example.com:4000" - uri = URI.parse("http://#{endpoint}") - - assert SocketInfo.origin(uri) == endpoint - end - end - - describe "touch" do - test "will update the TTL" do - endpoint = "example.com:4000" - socket = SocketInfo.build("ws://#{endpoint}") - Process.sleep(2) - touched_socket = SocketInfo.touch(socket) - - assert socket.connected_until < touched_socket.connected_until - end - end - - describe "expired?" do - setup do - start_supervised( - {Pleroma.Web.FedSockets.Supervisor, - [ - ping_interval: 8, - connection_duration: 5, - rejection_duration: 5, - fed_socket_rejections: [lazy: true] - ]} - ) - - :ok - end - - test "tests if the TTL is exceeded" do - endpoint = "example.com:4000" - socket = SocketInfo.build("ws://#{endpoint}") - refute SocketInfo.expired?(socket) - Process.sleep(10) - - assert SocketInfo.expired?(socket) - end - end - - describe "creating outgoing connection records" do - test "can be passed a string" do - assert %{conn_pid: :pid, origin: _origin} = SocketInfo.build("example.com:4000", :pid) - end - - test "can be passed a URI" do - uri = URI.parse("http://example.com:4000") - assert %{conn_pid: :pid, origin: origin} = SocketInfo.build(uri, :pid) - assert origin =~ "example.com:4000" - end - - test "will include the port number" do - assert %{conn_pid: :pid, origin: origin} = SocketInfo.build("http://example.com:4000", :pid) - - assert origin =~ ":4000" - end - - test "will provide the port if missing" do - assert %{conn_pid: :pid, origin: "example.com:80"} = - SocketInfo.build("http://example.com", :pid) - - assert %{conn_pid: :pid, origin: "example.com:443"} = - SocketInfo.build("https://example.com", :pid) - end - end - - describe "creating incoming connection records" do - test "can be passed a string" do - assert %{pid: _, origin: _origin} = SocketInfo.build("example.com:4000") - end - - test "can be passed a URI" do - uri = URI.parse("example.com:4000") - assert %{pid: _, origin: _origin} = SocketInfo.build(uri) - end - - test "will include the port number" do - assert %{pid: _, origin: origin} = SocketInfo.build("http://example.com:4000") - - assert origin =~ ":4000" - end - - test "will provide the port if missing" do - assert %{pid: _, origin: "example.com:80"} = SocketInfo.build("http://example.com") - assert %{pid: _, origin: "example.com:443"} = SocketInfo.build("https://example.com") - end - end -end diff --git a/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs index d36bb1ae8..5ef39bdb2 100644 --- a/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs @@ -45,21 +45,77 @@ defmacro assert_error_when_disable_push(do: yield) do end end - describe "creates push subscription" do - test "returns error when push disabled ", %{conn: conn} do + describe "when disabled" do + test "POST returns error", %{conn: conn} do assert_error_when_disable_push do conn - |> post("/api/v1/push/subscription", %{subscription: @sub}) + |> post("/api/v1/push/subscription", %{ + "data" => %{"alerts" => %{"mention" => true}}, + "subscription" => @sub + }) |> json_response_and_validate_schema(403) end end + test "GET returns error", %{conn: conn} do + assert_error_when_disable_push do + conn + |> get("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(403) + end + end + + test "PUT returns error", %{conn: conn} do + assert_error_when_disable_push do + conn + |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) + |> json_response_and_validate_schema(403) + end + end + + test "DELETE returns error", %{conn: conn} do + assert_error_when_disable_push do + conn + |> delete("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(403) + end + end + end + + describe "creates push subscription" do + test "ignores unsupported types", %{conn: conn} do + result = + conn + |> post("/api/v1/push/subscription", %{ + "data" => %{ + "alerts" => %{ + "fake_unsupported_type" => true + } + }, + "subscription" => @sub + }) + |> json_response_and_validate_schema(200) + + refute %{ + "alerts" => %{ + "fake_unsupported_type" => true + } + } == result + end + test "successful creation", %{conn: conn} do result = conn |> post("/api/v1/push/subscription", %{ "data" => %{ - "alerts" => %{"mention" => true, "test" => true, "pleroma:chat_mention" => true} + "alerts" => %{ + "mention" => true, + "favourite" => true, + "follow" => true, + "reblog" => true, + "pleroma:chat_mention" => true, + "pleroma:emoji_reaction" => true + } }, "subscription" => @sub }) @@ -68,7 +124,14 @@ test "successful creation", %{conn: conn} do [subscription] = Pleroma.Repo.all(Subscription) assert %{ - "alerts" => %{"mention" => true, "pleroma:chat_mention" => true}, + "alerts" => %{ + "mention" => true, + "favourite" => true, + "follow" => true, + "reblog" => true, + "pleroma:chat_mention" => true, + "pleroma:emoji_reaction" => true + }, "endpoint" => subscription.endpoint, "id" => to_string(subscription.id), "server_key" => @server_key @@ -77,14 +140,6 @@ test "successful creation", %{conn: conn} do end describe "gets a user subscription" do - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> get("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(403) - end - end - test "returns error when user hasn't subscription", %{conn: conn} do res = conn @@ -124,30 +179,47 @@ test "returns a user subsciption", %{conn: conn, user: user, token: token} do insert(:push_subscription, user: user, token: token, - data: %{"alerts" => %{"mention" => true}} + data: %{ + "alerts" => %{ + "mention" => true, + "favourite" => true, + "follow" => true, + "reblog" => true, + "pleroma:chat_mention" => true, + "pleroma:emoji_reaction" => true + } + } ) %{conn: conn, user: user, token: token, subscription: subscription} end - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) - |> json_response_and_validate_schema(403) - end - end - test "returns updated subsciption", %{conn: conn, subscription: subscription} do res = conn |> put("/api/v1/push/subscription", %{ - data: %{"alerts" => %{"mention" => false, "follow" => true}} + data: %{ + "alerts" => %{ + "mention" => false, + "favourite" => false, + "follow" => false, + "reblog" => false, + "pleroma:chat_mention" => false, + "pleroma:emoji_reaction" => false + } + } }) |> json_response_and_validate_schema(200) expect = %{ - "alerts" => %{"follow" => true, "mention" => false}, + "alerts" => %{ + "mention" => false, + "favourite" => false, + "follow" => false, + "reblog" => false, + "pleroma:chat_mention" => false, + "pleroma:emoji_reaction" => false + }, "endpoint" => "https://example.com/example/1234", "id" => to_string(subscription.id), "server_key" => @server_key @@ -158,14 +230,6 @@ test "returns updated subsciption", %{conn: conn, subscription: subscription} do end describe "deletes the user subscription" do - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> delete("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(403) - end - end - test "returns error when user hasn't subscription", %{conn: conn} do res = conn diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs index 7d8cc999a..2a4a8fd06 100644 --- a/test/pleroma/web/push/impl_test.exs +++ b/test/pleroma/web/push/impl_test.exs @@ -184,6 +184,24 @@ test "renders title and body for like activity" do "New Favorite" end + test "renders title and body for pleroma:emoji_reaction activity" do + user = insert(:user, nickname: "Bob") + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "This post is a really good post!" + }) + + {:ok, activity} = CommonAPI.react_with_emoji(activity.id, user, "👍") + object = Object.normalize(activity) + + assert Impl.format_body(%{activity: activity, type: "pleroma:emoji_reaction"}, user, object) == + "@Bob reacted with 👍" + + assert Impl.format_title(%{activity: activity, type: "pleroma:emoji_reaction"}) == + "New Reaction" + end + test "renders title for create activity with direct visibility" do user = insert(:user, nickname: "Bob")