- Account backup
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`
+- The site title is now injected as a `title` tag like preloads or metadata.
### Changed
- Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option).
- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`.
- Polls now always return a `voters_count`, even if they are single-choice
+- Admin Emails: The ap id is used as the user link in emails now.
<details>
<summary>API Changes</summary>
ENV MIX_ENV=prod
-RUN apk add git gcc g++ musl-dev make cmake &&\
+RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
echo "import Mix.Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
mix local.rebar --force &&\
use Mix.Config
-alias Pleroma.Docs.Generator
websocket_config = [
path: "/websocket",
}
]
},
- %{
- group: :pleroma,
- key: :mrf,
- tab: :mrf,
- label: "MRF",
- type: :group,
- description: "General MRF settings",
- children: [
- %{
- key: :policies,
- type: [:module, {:list, :module}],
- description:
- "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
- suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
- },
- %{
- key: :transparency,
- label: "MRF transparency",
- type: :boolean,
- description:
- "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
- },
- %{
- key: :transparency_exclusions,
- label: "MRF transparency exclusions",
- type: {:list, :string},
- description:
- "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
- suggestions: [
- "exclusion.com"
- ]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_simple,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
- label: "MRF Simple",
- type: :group,
- description: "Simple ingress policies",
- children: [
- %{
- key: :media_removal,
- type: {:list, :string},
- description: "List of instances to strip media attachments from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :media_nsfw,
- label: "Media NSFW",
- type: {:list, :string},
- description: "List of instances to tag all media as NSFW (sensitive) from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :federated_timeline_removal,
- type: {:list, :string},
- description:
- "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :reject,
- type: {:list, :string},
- description: "List of instances to reject activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :accept,
- type: {:list, :string},
- description: "List of instances to only accept activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :followers_only,
- type: {:list, :string},
- description: "Force posts from the given instances to be visible by followers only",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :report_removal,
- type: {:list, :string},
- description: "List of instances to reject reports from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :avatar_removal,
- type: {:list, :string},
- description: "List of instances to strip avatars from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :banner_removal,
- type: {:list, :string},
- description: "List of instances to strip banners from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :reject_deletes,
- type: {:list, :string},
- description: "List of instances to reject deletions from",
- suggestions: ["example.com", "*.example.com"]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_activity_expiration,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
- label: "MRF Activity Expiration Policy",
- type: :group,
- description: "Adds automatic expiration to all local activities",
- children: [
- %{
- key: :days,
- type: :integer,
- description: "Default global expiration time for all local activities (in days)",
- suggestions: [90, 365]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_subchain,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
- label: "MRF Subchain",
- type: :group,
- description:
- "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
- " All criteria are configured as a map of regular expressions to lists of policy modules.",
- children: [
- %{
- key: :match_actor,
- type: {:map, {:list, :string}},
- description: "Matches a series of regular expressions against the actor field",
- suggestions: [
- %{
- ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
- }
- ]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_rejectnonpublic,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
- description: "RejectNonPublic drops posts with non-public visibility settings.",
- label: "MRF Reject Non Public",
- type: :group,
- children: [
- %{
- key: :allow_followersonly,
- label: "Allow followers-only",
- type: :boolean,
- description: "Whether to allow followers-only posts"
- },
- %{
- key: :allow_direct,
- type: :boolean,
- description: "Whether to allow direct messages"
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_hellthread,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
- label: "MRF Hellthread",
- type: :group,
- description: "Block messages with excessive user mentions",
- children: [
- %{
- key: :delist_threshold,
- type: :integer,
- description:
- "Number of mentioned users after which the message gets removed from timelines and" <>
- "disables notifications. Set to 0 to disable.",
- suggestions: [10]
- },
- %{
- key: :reject_threshold,
- type: :integer,
- description:
- "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
- suggestions: [20]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_keyword,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
- label: "MRF Keyword",
- type: :group,
- description:
- "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
- children: [
- %{
- key: :reject,
- type: {:list, :string},
- description: """
- A list of patterns which result in message being rejected.
-
- Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
- """,
- suggestions: ["foo", ~r/foo/iu]
- },
- %{
- key: :federated_timeline_removal,
- type: {:list, :string},
- description: """
- A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
-
- Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
- """,
- suggestions: ["foo", ~r/foo/iu]
- },
- %{
- key: :replace,
- type: {:list, :tuple},
- description: """
- **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
-
- **Replacement**: a string. Leaving the field empty is permitted.
- """
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_mention,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
- label: "MRF Mention",
- type: :group,
- description: "Block messages which mention a specific user",
- children: [
- %{
- key: :actors,
- type: {:list, :string},
- description: "A list of actors for which any post mentioning them will be dropped",
- suggestions: ["actor1", "actor2"]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_vocabulary,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
- label: "MRF Vocabulary",
- type: :group,
- description: "Filter messages which belong to certain activity vocabularies",
- children: [
- %{
- key: :accept,
- type: {:list, :string},
- description:
- "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
- suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
- },
- %{
- key: :reject,
- type: {:list, :string},
- description:
- "A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
- suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
- }
- ]
- },
- # %{
- # group: :pleroma,
- # key: :mrf_user_allowlist,
- # tab: :mrf,
- # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
- # type: :map,
- # description:
- # "The keys in this section are the domain names that the policy should apply to." <>
- # " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID",
- # suggestions: [
- # %{"example.org" => ["https://example.org/users/admin"]}
- # ]
- # ]
- # },
%{
group: :pleroma,
key: :media_proxy,
}
]
},
- %{
- group: :pleroma,
- key: :mrf_normalize_markup,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
- label: "MRF Normalize Markup",
- description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
- type: :group,
- children: [
- %{
- key: :scrub_policy,
- type: :module,
- suggestions: [Pleroma.HTML.Scrubber.Default]
- }
- ]
- },
%{
group: :pleroma,
key: Pleroma.User,
}
]
},
- %{
- group: :pleroma,
- key: :mrf_object_age,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
- label: "MRF Object Age",
- type: :group,
- description:
- "Rejects or delists posts based on their timestamp deviance from your server's clock.",
- children: [
- %{
- key: :threshold,
- type: :integer,
- description: "Required age (in seconds) of a post before actions are taken.",
- suggestions: [172_800]
- },
- %{
- key: :actions,
- type: {:list, :atom},
- description:
- "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
- "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
- "`:reject` rejects the message entirely",
- suggestions: [:delist, :strip_followers, :reject]
- }
- ]
- },
%{
group: :pleroma,
key: :modules,
+++ /dev/null
-# Generate release environment file
-
-```sh tab="OTP"
- ./bin/pleroma_ctl release_env gen
-```
-
-```sh tab="From Source"
-mix pleroma.release_env gen
-```
--- /dev/null
+# Optimizing the BEAM
+
+Pleroma is built upon the Erlang/OTP VM known as BEAM. The BEAM VM is highly optimized for latency, but this has drawbacks in environments without dedicated hardware. One of the tricks used by the BEAM VM is [busy waiting](https://en.wikipedia.org/wiki/Busy_waiting). This allows the application to pretend to be busy working so the OS kernel does not pause the application process and switch to another process waiting for the CPU to execute its workload. It does this by spinning for a period of time which inflates the apparent CPU usage of the application so it is immediately ready to execute another task. This can be observed with utilities like **top(1)** which will show consistently high CPU usage for the process. Switching between procesess is a rather expensive operation and also clears CPU caches further affecting latency and performance. The goal of busy waiting is to avoid this penalty.
+
+This strategy is very successful in making a performant and responsive application, but is not desirable on Virtual Machines or hardware with few CPU cores. Pleroma instances are often deployed on the same server as the required PostgreSQL database which can lead to situations where the Pleroma application is holding the CPU in a busy-wait loop and as a result the database cannot process requests in a timely manner. The fewer CPUs available, the more this problem is exacerbated. The latency is further amplified by the OS being installed on a Virtual Machine as the Hypervisor uses CPU time-slicing to pause the entire OS and switch between other tasks.
+
+More adventurous admins can be creative with CPU affinity (e.g., *taskset* for Linux and *cpuset* on FreeBSD) to pin processes to specific CPUs and eliminate much of this contention. The most important advice is to run as few processes as possible on your server to achieve the best performance. Even idle background processes can occasionally create [software interrupts](https://en.wikipedia.org/wiki/Interrupt) and take attention away from the executing process creating latency spikes and invalidation of the CPU caches as they must be cleared when switching between processes for security.
+
+Please only change these settings if you are experiencing issues or really know what you are doing. In general, there's no need to change these settings.
+
+## VPS Provider Recommendations
+
+### Good
+
+* Hetzner Cloud
+
+### Bad
+
+* AWS (known to use burst scheduling)
+
+
+## Example configurations
+
+Tuning the BEAM requires you provide a config file normally called [vm.args](http://erlang.org/doc/man/erl.html#emulator-flags). If you are using systemd to manage the service you can modify the unit file as such:
+
+`ExecStart=/usr/bin/elixir --erl '-args_file /opt/pleroma/config/vm.args' -S /usr/bin/mix phx.server`
+
+Check your OS documentation to adopt a similar strategy on other platforms.
+
+### Virtual Machine and/or few CPU cores
+
+Disable the busy-waiting. This should generally only be done if you're on a platform that does burst scheduling, like AWS.
+
+**vm.args:**
+
+```
++sbwt none
++sbwtdcpu none
++sbwtdio none
+```
+
+### Dedicated Hardware
+
+Enable more busy waiting, increase the internal maximum limit of BEAM processes and ports. You can use this if you run on dedicated hardware, but it is not necessary.
+
+**vm.args:**
+
+```
++P 16777216
++Q 16777216
++K true
++A 128
++sbt db
++sbwt very_long
++swt very_low
++sub true
++Mulmbcs 32767
++Mumbcgs 1
++Musmbcs 2047
+```
+
+## Additional Reading
+
+* [WhatsApp: Scaling to Millions of Simultaneous Connections](https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf)
+* [Preemptive Scheduling and Spinlocks](https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF3150/h03/annet/slides/preemptive.pdf)
+* [The Curious Case of BEAM CPU Usage](https://stressgrid.com/blog/beam_cpu_usage/)
## Auth-related configuration, OAuth consumer mode etc.
See `Authentication` section of [the configuration cheatsheet](configuration/cheatsheet.md#authentication).
+
+## MRF policies descriptions
+
+If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns map with special structure.
+
+Example:
+
+```elixir
+%{
+ key: :mrf_activity_expiration,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
+ label: "MRF Activity Expiration Policy",
+ description: "Adds automatic expiration to all local activities",
+ children: [
+ %{
+ key: :days,
+ type: :integer,
+ description: "Default global expiration time for all local activities (in days)",
+ suggestions: [90, 365]
+ }
+ ]
+ }
+```
```
* Edit the service file and make sure that all paths fit your installation
-* Check that `EnvironmentFile` contains the correct path to the env file. Or generate the env file: `sudo -Hu pleroma mix pleroma.release_env gen`
* Enable and start `pleroma.service`:
```shell
# Run the config generator
su pleroma -s $SHELL -lc "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql"
-# Run the environment file generator.
-su pleroma -s $SHELL -lc "./bin/pleroma_ctl release_env gen"
-
# Create the postgres database
su postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql"
directory=/opt/pleroma
healthcheck_delay=60
healthcheck_timer=30
-export $(cat /opt/pleroma/config/pleroma.env)
: ${pleroma_port:-4000}
Environment="HOME=/var/lib/pleroma"
; Path to the folder containing the Pleroma installation.
WorkingDirectory=/opt/pleroma
-; Path to the environment file. the file contains RELEASE_COOKIE and etc
-EnvironmentFile=/opt/pleroma/config/pleroma.env
; Path to the Mix binary.
ExecStart=/usr/bin/mix phx.server
listen_port: :string,
strip_uploads: :string,
anonymize_uploads: :string,
- dedupe_uploads: :string,
- skip_release_env: :boolean,
- release_env_file: :string
+ dedupe_uploads: :string
],
aliases: [
o: :output,
write_robots_txt(static_dir, indexable, template_dir)
- if Keyword.get(options, :skip_release_env, false) do
- shell_info("""
- Release environment file is skip. Please generate the release env file before start.
- `MIX_ENV=#{Mix.env()} mix pleroma.release_env gen`
- """)
- else
- shell_info("Generation the environment file:")
-
- release_env_args =
- with path when not is_nil(path) <- Keyword.get(options, :release_env_file) do
- ["gen", "--path", path]
- else
- _ -> ["gen"]
- end
-
- Mix.Tasks.Pleroma.ReleaseEnv.run(release_env_args)
- end
-
shell_info(
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Mix.Tasks.Pleroma.ReleaseEnv do
- use Mix.Task
- import Mix.Pleroma
-
- @shortdoc "Generate Pleroma environment file."
- @moduledoc File.read!("docs/administration/CLI_tasks/release_environments.md")
-
- def run(["gen" | rest]) do
- {options, [], []} =
- OptionParser.parse(
- rest,
- strict: [
- force: :boolean,
- path: :string
- ],
- aliases: [
- p: :path,
- f: :force
- ]
- )
-
- file_path =
- get_option(
- options,
- :path,
- "Environment file path",
- "./config/pleroma.env"
- )
-
- env_path = Path.expand(file_path)
-
- proceed? =
- if File.exists?(env_path) do
- get_option(
- options,
- :force,
- "Environment file already exists. Do you want to overwrite the #{env_path} file? (y/n)",
- "n"
- ) === "y"
- else
- true
- end
-
- if proceed? do
- case do_generate(env_path) do
- {:error, reason} ->
- shell_error(
- File.Error.message(%{action: "write to file", reason: reason, path: env_path})
- )
-
- _ ->
- shell_info("\nThe file generated: #{env_path}.\n")
-
- shell_info("""
- WARNING: before start pleroma app please make sure to make the file read-only and non-modifiable.
- Example:
- chmod 0444 #{file_path}
- chattr +i #{file_path}
- """)
- end
- else
- shell_info("\nThe file is exist. #{env_path}.\n")
- end
- end
-
- def do_generate(path) do
- content = "RELEASE_COOKIE=#{Base.encode32(:crypto.strong_rand_bytes(32))}"
-
- File.mkdir_p!(Path.dirname(path))
- File.write(path, content)
- end
-end
@spec compile :: :ok
def compile do
- :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions))
+ descriptions =
+ Pleroma.Web.ActivityPub.MRF.config_descriptions()
+ |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
+
+ :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions))
end
@spec compiled_descriptions :: Map.t()
Keyword.get(instance_config(), :notify_email, instance_config()[:email])
end
- defp user_url(user) do
- Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
- end
-
def test_email(mail_to \\ nil) do
html_body = """
<h3>Instance Test Email</h3>
end
html_body = """
- <p>Reported by: <a href="#{user_url(reporter)}">#{reporter.nickname}</a></p>
- <p>Reported Account: <a href="#{user_url(account)}">#{account.nickname}</a></p>
+ <p>Reported by: <a href="#{reporter.ap_id}">#{reporter.nickname}</a></p>
+ <p>Reported Account: <a href="#{account.ap_id}">#{account.nickname}</a></p>
#{comment_html}
#{statuses_html}
<p>
def new_unapproved_registration(to, account) do
html_body = """
- <p>New account for review: <a href="#{user_url(account)}">@#{account.nickname}</a></p>
+ <p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
"""
|> sign_fetch(id, date)
case HTTP.get(id, headers) do
- {:ok, %{body: body, status: code}} when code in 200..299 ->
- {:ok, body}
+ {:ok, %{body: body, status: code, headers: headers}} when code in 200..299 ->
+ case List.keyfind(headers, "content-type", 0) do
+ {_, content_type} ->
+ case Plug.Conn.Utils.media_type(content_type) do
+ {:ok, "application", "activity+json", _} ->
+ {:ok, body}
+
+ {:ok, "application", "ld+json",
+ %{"profile" => "https://www.w3.org/ns/activitystreams"}} ->
+ {:ok, body}
+
+ _ ->
+ {:error, {:content_type, content_type}}
+ end
+
+ _ ->
+ {:error, {:content_type, nil}}
+ end
{:ok, %{status: code}} when code in [404, 410] ->
{:error, "Object has been deleted"}
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF do
+ require Logger
+
+ @mrf_config_descriptions [
+ %{
+ group: :pleroma,
+ key: :mrf,
+ tab: :mrf,
+ label: "MRF",
+ type: :group,
+ description: "General MRF settings",
+ children: [
+ %{
+ key: :policies,
+ type: [:module, {:list, :module}],
+ description:
+ "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
+ suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
+ },
+ %{
+ key: :transparency,
+ label: "MRF transparency",
+ type: :boolean,
+ description:
+ "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
+ },
+ %{
+ key: :transparency_exclusions,
+ label: "MRF transparency exclusions",
+ type: {:list, :string},
+ description:
+ "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+ suggestions: [
+ "exclusion.com"
+ ]
+ }
+ ]
+ }
+ ]
+
+ @default_description %{
+ label: "",
+ description: ""
+ }
+
+ @required_description_keys [:key, :related_policy]
+
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
+ @callback describe() :: {:ok | :error, Map.t()}
+ @callback config_description() :: %{
+ optional(:children) => [map()],
+ key: atom(),
+ related_policy: String.t(),
+ label: String.t(),
+ description: String.t()
+ }
+ @optional_callbacks config_description: 0
def filter(policies, %{} = message) do
policies
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
end
- @callback describe() :: {:ok | :error, Map.t()}
-
def describe(policies) do
{:ok, policy_configs} =
policies
end
def describe, do: get_policies() |> describe()
+
+ def config_descriptions do
+ Pleroma.Web.ActivityPub.MRF
+ |> Pleroma.Docs.Generator.list_behaviour_implementations()
+ |> config_descriptions()
+ end
+
+ def config_descriptions(policies) do
+ Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc ->
+ if function_exported?(policy, :config_description, 0) do
+ description =
+ @default_description
+ |> Map.merge(policy.config_description)
+ |> Map.put(:group, :pleroma)
+ |> Map.put(:tab, :mrf)
+ |> Map.put(:type, :group)
+
+ if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do
+ [description | acc]
+ else
+ Logger.warn(
+ "#{policy} config description doesn't have one or all required keys #{
+ inspect(@required_description_keys)
+ }"
+ )
+
+ acc
+ end
+ else
+ Logger.debug(
+ "#{policy} is excluded from config descriptions, because does not implement `config_description/0` method."
+ )
+
+ acc
+ end
+ end)
+ end
end
_ -> Map.put(activity, "expires_at", expires_at)
end
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_activity_expiration,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
+ label: "MRF Activity Expiration Policy",
+ description: "Adds automatic expiration to all local activities",
+ children: [
+ %{
+ key: :days,
+ type: :integer,
+ description: "Default global expiration time for all local activities (in days)",
+ suggestions: [90, 365]
+ }
+ ]
+ }
+ end
end
@impl true
def describe,
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_hellthread,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
+ label: "MRF Hellthread",
+ description: "Block messages with excessive user mentions",
+ children: [
+ %{
+ key: :delist_threshold,
+ type: :integer,
+ description:
+ "Number of mentioned users after which the message gets removed from timelines and" <>
+ "disables notifications. Set to 0 to disable.",
+ suggestions: [10]
+ },
+ %{
+ key: :reject_threshold,
+ type: :integer,
+ description:
+ "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
+ suggestions: [20]
+ }
+ ]
+ }
+ end
end
{:ok, %{mrf_keyword: mrf_keyword}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_keyword,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
+ label: "MRF Keyword",
+ description:
+ "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
+ children: [
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description: """
+ A list of patterns which result in message being rejected.
+
+ Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+ """,
+ suggestions: ["foo", ~r/foo/iu]
+ },
+ %{
+ key: :federated_timeline_removal,
+ type: {:list, :string},
+ description: """
+ A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
+
+ Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+ """,
+ suggestions: ["foo", ~r/foo/iu]
+ },
+ %{
+ key: :replace,
+ type: {:list, :tuple},
+ description: """
+ **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+
+ **Replacement**: a string. Leaving the field empty is permitted.
+ """
+ }
+ ]
+ }
+ end
end
@impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_mention,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
+ label: "MRF Mention",
+ description: "Block messages which mention a specific user",
+ children: [
+ %{
+ key: :actors,
+ type: {:list, :string},
+ description: "A list of actors for which any post mentioning them will be dropped",
+ suggestions: ["actor1", "actor2"]
+ }
+ ]
+ }
+ end
end
@behaviour Pleroma.Web.ActivityPub.MRF
+ @impl true
def filter(%{"type" => "Create", "object" => child_object} = object) do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
def filter(object), do: {:ok, object}
+ @impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_normalize_markup,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
+ label: "MRF Normalize Markup",
+ description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
+ children: [
+ %{
+ key: :scrub_policy,
+ type: :module,
+ suggestions: [Pleroma.HTML.Scrubber.Default]
+ }
+ ]
+ }
+ end
end
{:ok, %{mrf_object_age: mrf_object_age}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_object_age,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
+ label: "MRF Object Age",
+ description:
+ "Rejects or delists posts based on their timestamp deviance from your server's clock.",
+ children: [
+ %{
+ key: :threshold,
+ type: :integer,
+ description: "Required age (in seconds) of a post before actions are taken.",
+ suggestions: [172_800]
+ },
+ %{
+ key: :actions,
+ type: {:list, :atom},
+ description:
+ "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
+ "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
+ "`:reject` rejects the message entirely",
+ suggestions: [:delist, :strip_followers, :reject]
+ }
+ ]
+ }
+ end
end
@impl true
def describe,
do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_rejectnonpublic,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
+ description: "RejectNonPublic drops posts with non-public visibility settings.",
+ label: "MRF Reject Non Public",
+ children: [
+ %{
+ key: :allow_followersonly,
+ label: "Allow followers-only",
+ type: :boolean,
+ description: "Whether to allow followers-only posts"
+ },
+ %{
+ key: :allow_direct,
+ type: :boolean,
+ description: "Whether to allow direct messages"
+ }
+ ]
+ }
+ end
end
{:ok, %{mrf_simple: mrf_simple}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_simple,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
+ label: "MRF Simple",
+ description: "Simple ingress policies",
+ children: [
+ %{
+ key: :media_removal,
+ type: {:list, :string},
+ description: "List of instances to strip media attachments from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :media_nsfw,
+ label: "Media NSFW",
+ type: {:list, :string},
+ description: "List of instances to tag all media as NSFW (sensitive) from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :federated_timeline_removal,
+ type: {:list, :string},
+ description:
+ "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description: "List of instances to reject activities from (except deletes)",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :accept,
+ type: {:list, :string},
+ description: "List of instances to only accept activities from (except deletes)",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :followers_only,
+ type: {:list, :string},
+ description: "Force posts from the given instances to be visible by followers only",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :report_removal,
+ type: {:list, :string},
+ description: "List of instances to reject reports from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :avatar_removal,
+ type: {:list, :string},
+ description: "List of instances to strip avatars from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :banner_removal,
+ type: {:list, :string},
+ description: "List of instances to strip banners from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :reject_deletes,
+ type: {:list, :string},
+ description: "List of instances to reject deletions from",
+ suggestions: ["example.com", "*.example.com"]
+ }
+ ]
+ }
+ end
end
@impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_subchain,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
+ label: "MRF Subchain",
+ description:
+ "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
+ " All criteria are configured as a map of regular expressions to lists of policy modules.",
+ children: [
+ %{
+ key: :match_actor,
+ type: {:map, {:list, :string}},
+ description: "Matches a series of regular expressions against the actor field",
+ suggestions: [
+ %{
+ ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
+ }
+ ]
+ }
+ ]
+ }
+ end
end
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
end
+
+ # TODO: change way of getting settings on `lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex:18` to use `hosts` subkey
+ # @impl true
+ # def config_description do
+ # %{
+ # key: :mrf_user_allowlist,
+ # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
+ # description: "Accept-list of users from specified instances",
+ # children: [
+ # %{
+ # key: :hosts,
+ # type: :map,
+ # description:
+ # "The keys in this section are the domain names that the policy should apply to." <>
+ # " Each key should be assigned a list of users that should be allowed " <>
+ # "through by their ActivityPub ID",
+ # suggestions: [%{"example.org" => ["https://example.org/users/admin"]}]
+ # }
+ # ]
+ # }
+ # end
end
@behaviour Pleroma.Web.ActivityPub.MRF
+ @impl true
def filter(%{"type" => "Undo", "object" => child_message} = message) do
with {:ok, _} <- filter(child_message) do
{:ok, message}
def filter(message), do: {:ok, message}
+ @impl true
def describe,
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_vocabulary,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
+ label: "MRF Vocabulary",
+ description: "Filter messages which belong to certain activity vocabularies",
+ children: [
+ %{
+ key: :accept,
+ type: {:list, :string},
+ description:
+ "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
+ suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
+ },
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description:
+ "A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
+ suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
+ }
+ ]
+ }
+ end
end
field(:type, :string)
field(:mediaType, :string, default: "application/octet-stream")
field(:name, :string)
+ field(:blurhash, :string)
embeds_many :url, UrlObjectValidator, primary_key: false do
field(:type, :string)
|> fix_url()
struct
- |> cast(data, [:type, :mediaType, :name])
+ |> cast(data, [:type, :mediaType, :name, :blurhash])
|> cast_embed(:url, with: &url_changeset/2)
|> validate_inclusion(:type, ~w[Link Document Audio Image Video])
|> validate_required([:type, :mediaType, :url])
}
|> Maps.put_if_present("mediaType", media_type)
|> Maps.put_if_present("name", data["name"])
+ |> Maps.put_if_present("blurhash", data["blurhash"])
else
nil
end
tags = build_tags(conn, params)
preloads = preload_data(conn, params)
+ title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
response =
index_content
- |> String.replace("<!--server-generated-meta-->", tags <> preloads)
+ |> String.replace("<!--server-generated-meta-->", tags <> preloads <> title)
conn
|> put_resp_content_type("text/html")
def redirector_with_preload(conn, params) do
{:ok, index_content} = File.read(index_file_path())
preloads = preload_data(conn, params)
+ title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
response =
index_content
- |> String.replace("<!--server-generated-meta-->", preloads)
+ |> String.replace("<!--server-generated-meta-->", preloads <> title)
conn
|> put_resp_content_type("text/html")
def activity_content(_), do: ""
- def activity_context(activity), do: activity.data["context"]
+ def activity_context(activity), do: escape(activity.data["context"])
def attachment_href(attachment) do
attachment["url"]
text_url: href,
type: type,
description: attachment["name"],
- pleroma: %{mime_type: media_type}
+ pleroma: %{mime_type: media_type},
+ blurhash: attachment["blurhash"]
}
end
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
<%= if @data["summary"] do %>
- <summary><%= @data["summary"] %></summary>
+ <summary><%= escape(@data["summary"]) %></summary>
<% end %>
<%= if @activity.local do %>
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
<%= if @data["summary"] do %>
- <description><%= @data["summary"] %></description>
+ <description><%= escape(@data["summary"]) %></description>
<% end %>
<%= if @activity.local do %>
--- /dev/null
+defmodule Pleroma.Repo.Migrations.RemovePurgeExpiredActivityWorkerFromObanConfig do
+ use Ecto.Migration
+
+ def change do
+ with %Pleroma.ConfigDB{} = config <-
+ Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Oban}),
+ crontab when is_list(crontab) <- config.value[:crontab],
+ index when is_integer(index) <-
+ Enum.find_index(crontab, fn {_, worker} ->
+ worker == Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker
+ end) do
+ updated_value = Keyword.put(config.value, :crontab, List.delete_at(crontab, index))
+
+ config
+ |> Ecto.Changeset.change(value: updated_value)
+ |> Pleroma.Repo.update()
+ end
+ end
+end
-<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1600365488745.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.826c44232e0a76bbd9ba.js></script></body></html>
\ No newline at end of file
+<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1600365488745.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.826c44232e0a76bbd9ba.js></script></body></html>
--- /dev/null
+defmodule Fixtures.Modules.GoodMRF do
+ @behaviour Pleroma.Web.ActivityPub.MRF
+
+ @impl true
+ def filter(a), do: {:ok, a}
+
+ @impl true
+ def describe, do: %{}
+
+ @impl true
+ def config_description do
+ %{
+ key: :good_mrf,
+ related_policy: "Fixtures.Modules.GoodMRF",
+ label: "Good MRF",
+ description: "Some description"
+ }
+ end
+end
--- /dev/null
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://patch.cx/schemas/litepub-0.1.jsonld",
+ {
+ "@language": "und"
+ }
+ ],
+ "actor": "https://patch.cx/users/rin",
+ "attachment": [],
+ "attributedTo": "https://patch.cx/users/rin",
+ "cc": [
+ "https://patch.cx/users/rin/followers"
+ ],
+ "content": "Oracle Corporation (NYSE: ORCL) today announced that it has signed a definitive merger agreement to acquire Pleroma AG (FRA: PLA), for $26.50 per share (approximately $10.3 billion). The transaction has been approved by the boards of directors of both companies and should close by early January.",
+ "context": "https://patch.cx/contexts/spoof",
+ "id": "https://patch.cx/objects/spoof",
+ "published": "2020-10-23T18:02:06.038856Z",
+ "sensitive": false,
+ "summary": "Oracle buys Pleroma",
+ "tag": [],
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type": "Note"
+}
defmodule Mix.Tasks.Pleroma.InstanceTest do
use ExUnit.Case
- @release_env_file "./test/pleroma.test.env"
-
setup do
File.mkdir_p!(tmp_path())
File.rm_rf(Path.join(static_dir, "robots.txt"))
end
- if File.exists?(@release_env_file), do: File.rm_rf(@release_env_file)
-
Pleroma.Config.put([:instance, :static_dir], static_dir)
end)
"--dedupe-uploads",
"n",
"--anonymize-uploads",
- "n",
- "--release-env-file",
- @release_env_file
+ "n"
])
end
assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]"
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
- assert File.exists?(@release_env_file)
-
- assert File.read!(@release_env_file) =~ ~r/^RELEASE_COOKIE=.*/
end
defp generated_setup_psql do
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Mix.Tasks.Pleroma.ReleaseEnvTest do
- use ExUnit.Case
- import ExUnit.CaptureIO, only: [capture_io: 1]
-
- @path "config/pleroma.test.env"
-
- def do_clean do
- if File.exists?(@path) do
- File.rm_rf(@path)
- end
- end
-
- setup do
- do_clean()
- on_exit(fn -> do_clean() end)
- :ok
- end
-
- test "generate pleroma.env" do
- assert capture_io(fn ->
- Mix.Tasks.Pleroma.ReleaseEnv.run(["gen", "--path", @path, "--force"])
- end) =~ "The file generated"
-
- assert File.read!(@path) =~ "RELEASE_COOKIE="
- end
-end
AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
- reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id)
- account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
+ reporter_url = reporter.ap_id
+ account_url = account.ap_id
assert res.to == [{to_user.name, to_user.email}]
assert res.from == {config[:name], config[:notify_email]}
res = AdminEmail.new_unapproved_registration(to_user, account)
- account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
+ account_url = account.ap_id
assert res.to == [{to_user.name, to_user.email}]
assert res.from == {config[:name], config[:notify_email]}
%{method: :get, url: "https://mastodon.example.org/users/userisgone404"} ->
%Tesla.Env{status: 404}
+ %{
+ method: :get,
+ url:
+ "https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ headers: [{"content-type", "application/json"}],
+ body: File.read!("test/fixtures/spoofed-object.json")
+ }
+
env ->
apply(HttpRequestMock, :request, [env])
end)
%{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json")
+ body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
%{method: :get, url: "https://social.sakamoto.gq/users/eal"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/fetch_mocks/eal.json")
+ body: File.read!("test/fixtures/fetch_mocks/eal.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
%{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json")
+ body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
%{method: :get, url: "https://busshi.moe/users/tuxcrafting"} ->
"http://mastodon.example.org/@admin/99541947525187367"
)
end
+
+ test "it does not fetch a spoofed object uploaded on an instance as an attachment" do
+ assert {:error, _} =
+ Fetcher.fetch_object_from_id(
+ "https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
+ )
+ end
end
describe "implementation quirks" do
setup do
mock(fn
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/poll_original.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
+ }
env ->
apply(HttpRequestMock, :request, [env])
mock_modified.(%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
+ body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
mock_modified.(%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
+ body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
mock_modified.(%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
+ body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_hidden_counters/following" ->
- json(%{
- "@context" => "https://www.w3.org/ns/activitystreams",
- "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
- })
+ json(
+ %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
+ },
+ headers: HttpRequestMock.activitypub_object_headers()
+ )
"http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
%Tesla.Env{status: 403, body: ""}
"http://localhost:4001/users/masto_hidden_counters/followers" ->
- json(%{
- "@context" => "https://www.w3.org/ns/activitystreams",
- "id" => "http://localhost:4001/users/masto_hidden_counters/following"
- })
+ json(
+ %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => "http://localhost:4001/users/masto_hidden_counters/following"
+ },
+ headers: HttpRequestMock.activitypub_object_headers()
+ )
"http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
%Tesla.Env{status: 403, body: ""}
Tesla.Mock.mock(fn
%{method: :get, url: "https://princess.cat/users/mewmew"} ->
file = File.read!("test/fixtures/mewmew_no_name.json")
- %Tesla.Env{status: 200, body: file}
+ %Tesla.Env{status: 200, body: file, headers: HttpRequestMock.activitypub_object_headers()}
end)
{:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
{:ok, ^expected} = MRF.describe()
end
end
+
+ test "config_descriptions/0" do
+ descriptions = MRF.config_descriptions()
+
+ good_mrf = Enum.find(descriptions, fn %{key: key} -> key == :good_mrf end)
+
+ assert good_mrf == %{
+ key: :good_mrf,
+ related_policy: "Fixtures.Modules.GoodMRF",
+ label: "Good MRF",
+ description: "Some description",
+ group: :pleroma,
+ tab: :mrf,
+ type: :group
+ }
+ end
end
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
"type" => "Document",
"name" => nil,
- "mediaType" => "image/jpeg"
+ "mediaType" => "image/jpeg",
+ "blurhash" => "UD9jJz~VSbR#xT$~%KtQX9R,WAs9RjWBs:of"
}
{:ok, attachment} =
] = attachment.url
assert attachment.mediaType == "image/jpeg"
+ assert attachment.blurhash == "UD9jJz~VSbR#xT$~%KtQX9R,WAs9RjWBs:of"
end
test "it handles our own uploads" do
Tesla.Mock.mock(fn
%{method: :get} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/mastodon-note-object.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
+ }
end)
_user = insert(:user, local: false, ap_id: data["actor"])
test "Pterotype (Wordpress Plugin) Article" do
Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
+ }
end)
data =
%{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
%{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
end)
Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json")
+ body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
end)
%{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")
+ body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
end)
"mediaType" => "audio/ogg",
"type" => "Link",
"name" => nil,
+ "blurhash" => nil,
"url" => [
%{
"href" =>
%{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json")
+ body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
%{url: "https://mobilizon.org/@tcit"} ->
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json")
+ body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json"),
+ headers: HttpRequestMock.activitypub_object_headers()
}
end)
"type" => "Link",
"mediaType" => "video/mp4",
"name" => nil,
+ "blurhash" => nil,
"url" => [
%{
"href" =>
"type" => "Link",
"mediaType" => "video/mp4",
"name" => nil,
+ "blurhash" => nil,
"url" => [
%{
"href" =>
end
end
+ test "GET /*path adds a title", %{conn: conn} do
+ clear_config([:instance, :name], "a cool title")
+
+ assert conn
+ |> get("/")
+ |> html_response(200) =~ "<title>a cool title</title>"
+ end
+
describe "preloaded data and metadata attached to" do
test "GET /:maybe_nickname_or_id", %{conn: conn} do
+ clear_config([:instance, :name], "a cool title")
+
user = insert(:user)
user_missing = get(conn, "/foo")
user_present = get(conn, "/#{user.nickname}")
- assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->")
+ assert html_response(user_missing, 200) =~ "<!--server-generated-meta-->"
refute html_response(user_present, 200) =~ "<!--server-generated-meta-->"
assert html_response(user_present, 200) =~ "initial-results"
+ assert html_response(user_present, 200) =~ "<title>a cool title</title>"
end
test "GET /*path", %{conn: conn} do
describe "preloaded data is attached to" do
test "GET /main/public", %{conn: conn} do
+ clear_config([:instance, :name], "a cool title")
+
public_page = get(conn, "/main/public")
refute html_response(public_page, 200) =~ "<!--server-generated-meta-->"
assert html_response(public_page, 200) =~ "initial-results"
+ assert html_response(public_page, 200) =~ "<title>a cool title</title>"
end
test "GET /main/all", %{conn: conn} do
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Feed.FeedView
setup do: clear_config([:static_fe, :enabled], false)
describe "feed" do
setup do: clear_config([:feed])
- test "gets an atom feed", %{conn: conn} do
+ setup do
Config.put(
[:feed, :post_title],
- %{max_length: 10, omission: "..."}
+ %{max_length: 15, omission: "..."}
)
activity = insert(:note_activity)
note =
insert(:note,
data: %{
- "content" => "This is :moominmamma: note ",
+ "content" => "This & this is :moominmamma: note ",
+ "source" => "This & this is :moominmamma: note ",
"attachment" => [
%{
"url" => [
]
}
],
- "inReplyTo" => activity.data["id"]
+ "inReplyTo" => activity.data["id"],
+ "context" => "2hu & as",
+ "summary" => "2hu & as"
}
)
insert(:note,
user: user,
data: %{
- "content" => "42 This is :moominmamma: note ",
+ "content" => "42 & This is :moominmamma: note ",
"inReplyTo" => activity.data["id"]
}
)
note_activity2 = insert(:note_activity, note: note2)
object = Object.normalize(note_activity)
+ [user: user, object: object, max_id: note_activity2.id]
+ end
+
+ test "gets an atom feed", %{conn: conn, user: user, object: object, max_id: max_id} do
resp =
conn
|> put_req_header("accept", "application/atom+xml")
|> SweetXml.parse()
|> SweetXml.xpath(~x"//entry/title/text()"l)
- assert activity_titles == ['42 This...', 'This is...']
- assert resp =~ object.data["content"]
+ assert activity_titles == ['42 & Thi...', 'This & t...']
+ assert resp =~ FeedView.escape(object.data["content"])
+ assert resp =~ FeedView.escape(object.data["summary"])
+ assert resp =~ FeedView.escape(object.data["context"])
resp =
conn
|> put_req_header("accept", "application/atom+xml")
- |> get("/users/#{user.nickname}/feed", %{"max_id" => note_activity2.id})
+ |> get("/users/#{user.nickname}/feed", %{"max_id" => max_id})
|> response(200)
activity_titles =
|> SweetXml.parse()
|> SweetXml.xpath(~x"//entry/title/text()"l)
- assert activity_titles == ['This is...']
+ assert activity_titles == ['This & t...']
end
- test "gets a rss feed", %{conn: conn} do
- Pleroma.Config.put(
- [:feed, :post_title],
- %{max_length: 10, omission: "..."}
- )
-
- activity = insert(:note_activity)
-
- note =
- insert(:note,
- data: %{
- "content" => "This is :moominmamma: note ",
- "attachment" => [
- %{
- "url" => [
- %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}
- ]
- }
- ],
- "inReplyTo" => activity.data["id"]
- }
- )
-
- note_activity = insert(:note_activity, note: note)
- user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
- note2 =
- insert(:note,
- user: user,
- data: %{
- "content" => "42 This is :moominmamma: note ",
- "inReplyTo" => activity.data["id"]
- }
- )
-
- note_activity2 = insert(:note_activity, note: note2)
- object = Object.normalize(note_activity)
-
+ test "gets a rss feed", %{conn: conn, user: user, object: object, max_id: max_id} do
resp =
conn
|> put_req_header("accept", "application/rss+xml")
|> SweetXml.parse()
|> SweetXml.xpath(~x"//item/title/text()"l)
- assert activity_titles == ['42 This...', 'This is...']
- assert resp =~ object.data["content"]
+ assert activity_titles == ['42 & Thi...', 'This & t...']
+ assert resp =~ FeedView.escape(object.data["content"])
+ assert resp =~ FeedView.escape(object.data["summary"])
+ assert resp =~ FeedView.escape(object.data["context"])
resp =
conn
|> put_req_header("accept", "application/rss+xml")
- |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => note_activity2.id})
+ |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => max_id})
|> response(200)
activity_titles =
|> SweetXml.parse()
|> SweetXml.xpath(~x"//item/title/text()"l)
- assert activity_titles == ['This is...']
+ assert activity_titles == ['This & t...']
end
test "returns 404 for a missing feed", %{conn: conn} do
"href" => "someurl"
}
],
+ "blurhash" => "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn",
"uuid" => 6
}
preview_url: "someurl",
text_url: "someurl",
description: nil,
- pleroma: %{mime_type: "image/png"}
+ pleroma: %{mime_type: "image/png"},
+ blurhash: "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn"
}
api_spec = Pleroma.Web.ApiSpec.spec()
defmodule HttpRequestMock do
require Logger
+ def activitypub_object_headers, do: [{"content-type", "application/activity+json"}]
+
def request(
%Tesla.Env{
url: url,
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json")
+ body: File.read!("test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json")
+ body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/status.emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/status.emelie.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/emelie.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/rinpatch.json")
+ body: File.read!("test/fixtures/tesla_mock/rinpatch.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_attachment.json")
+ body: File.read!("test/fixtures/tesla_mock/poll_attachment.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json")
+ body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/lucifermysticus.json")
+ body: File.read!("test/fixtures/tesla_mock/lucifermysticus.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json")
+ body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json")
+ body: File.read!("test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/rye.json")
+ body: File.read!("test/fixtures/tesla_mock/rye.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/rye.json")
+ body: File.read!("test/fixtures/tesla_mock/rye.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/puckipedia.com.json")
+ body: File.read!("test/fixtures/tesla_mock/puckipedia.com.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/7even.json")
+ body: File.read!("test/fixtures/tesla_mock/7even.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/peertube.moe-vid.json")
+ body: File.read!("test/fixtures/tesla_mock/peertube.moe-vid.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https___framatube.org_accounts_framasoft.json")
+ body: File.read!("test/fixtures/tesla_mock/https___framatube.org_accounts_framasoft.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/framatube.org-video.json")
+ body: File.read!("test/fixtures/tesla_mock/framatube.org-video.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/craigmaloney.json")
+ body: File.read!("test/fixtures/tesla_mock/craigmaloney.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/peertube-social.json")
+ body: File.read!("test/fixtures/tesla_mock/peertube-social.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json")
+ body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json")
+ body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/wedistribute-article.json")
+ body: File.read!("test/fixtures/tesla_mock/wedistribute-article.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")
+ body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/admin@mastdon.example.org.json")
+ body: File.read!("test/fixtures/tesla_mock/admin@mastdon.example.org.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json")
+ body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json")
+ body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/mastodon-note-object.json")
+ body: File.read!("test/fixtures/mastodon-note-object.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mayumayu.json")
+ body: File.read!("test/fixtures/tesla_mock/mayumayu.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/mayumayupost.json")
+ body: File.read!("test/fixtures/tesla_mock/mayumayupost.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/winterdienst_webfinger.json")
+ body: File.read!("test/fixtures/tesla_mock/winterdienst_webfinger.json"),
+ headers: activitypub_object_headers()
}}
end
end
def get("https://mastodon.social/users/lambadalambda", _, _, _) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/lambadalambda.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("https://apfed.club/channel/indio", _, _, _) do
{:ok,
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json")}}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("https://social.heldscal.la/user/23211", _, _, [{"accept", "application/activity+json"}]) do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/masto_closed_followers.json")
+ body: File.read!("test/fixtures/users_mock/masto_closed_followers.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json")
+ body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/masto_closed_following.json")
+ body: File.read!("test/fixtures/users_mock/masto_closed_following.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json")
+ body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/friendica_followers.json")
+ body: File.read!("test/fixtures/users_mock/friendica_followers.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/friendica_following.json")
+ body: File.read!("test/fixtures/users_mock/friendica_following.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/pleroma_followers.json")
+ body: File.read!("test/fixtures/users_mock/pleroma_followers.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/users_mock/pleroma_following.json")
+ body: File.read!("test/fixtures/users_mock/pleroma_following.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity.json")
+ body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json")
+ body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json"),
+ headers: activitypub_object_headers()
}}
end
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json")
+ body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json"),
+ headers: activitypub_object_headers()
}}
end
end
def get("http://mastodon.example.org/@admin/99541947525187367", _, _, _) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-post-activity.json")}}
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/mastodon-post-activity.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("https://info.pleroma.site/activity4.json", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/tesla_mock/misskey_poll_no_end_date.json")
+ body: File.read!("test/fixtures/tesla_mock/misskey_poll_no_end_date.json"),
+ headers: activitypub_object_headers()
}}
end
end
def get("https://skippers-bin.com/users/7v1w1r8ce6", _, _, _) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sjw.json")}}
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/sjw.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("https://patch.cx/users/rin", _, _, _) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/rin.json")}}
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/rin.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get(
_
) do
{:ok,
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_audio.json")}}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/funkwhale_audio.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("https://channels.tests.funkwhale.audio/federation/actors/compositions", _, _, _) do
{:ok,
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")}}
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("http://example.com/rel_me/error", _, _, _) do
end
def get("https://relay.mastodon.host/actor", _, _, _) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/relay/relay.json")}}
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/relay/relay.json"),
+ headers: activitypub_object_headers()
+ }}
end
def get("http://localhost:4001/", _, "", [{"accept", "text/html"}]) do