Compare commits

..

No commits in common. "00f79c3a117244cb0ff6ffd02cab53e33de36d5c" and "204dbbc97c9421d08a7105c38bd43dfe2fd750df" have entirely different histories.

63 changed files with 328 additions and 753 deletions

View file

@ -4,36 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2023.04 ## Unreleased
## Added
- Nodeinfo keys for unauthenticated timeline visibility
- Option to disable federated timeline
- Option to make the bubble timeline publicly accessible
- Ability to swap between installed standard frontends
- *mastodon frontends are still not counted as standard frontends due to the complexity in serving them correctly*.
### Upgrade Notes
- Elixir 1.14 is now required. If your distribution does not package this, you can
use [asdf](https://asdf-vm.com/). At time of writing, elixir 1.14.3 / erlang 25.3
is confirmed to work.
## 2023.03
## Fixed ## Fixed
- Allowed contentMap to be updated on edit - Allowed contentMap to be updated on edit
- Filter creation now accepts expires\_at
### Changed ### Changed
- Restoring the database from a dump now goes much faster without need for work-arounds - Restoring the database from a dump now goes much faster without need for work-arounds
- Misskey reaction matching uses `content` parameter now
### Added ### Added
- Extend the mix task `prune_objects` with option `--prune-orphaned-activities` to also prune orphaned activities, allowing to reclaim even more database space - Extend the mix task `prune_objects` with option `--prune-orphaned-activities` to also prune orphaned activities, allowing to reclaim even more database space
### Removed ### Removed
- Possibility of using the `style` parameter on `span` elements. This will break certain MFM parameters. - Possibility of using the `style` parameter on `span` elements. This will break certain MFM parameters.
- Option for "default" image description.
## 2023.02 ## 2023.02

View file

@ -1,4 +1,4 @@
FROM hexpm/elixir:1.14.3-erlang-25.3-alpine-3.17.2 FROM hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
ENV MIX_ENV=prod ENV MIX_ENV=prod
ENV ERL_EPMD_ADDRESS=127.0.0.1 ENV ERL_EPMD_ADDRESS=127.0.0.1

View file

@ -54,9 +54,6 @@ If your platform is not supported, or you just want to be able to edit the sourc
### Docker ### Docker
Docker installation is supported via [this setup](https://docs.akkoma.dev/stable/installation/docker_en/) Docker installation is supported via [this setup](https://docs.akkoma.dev/stable/installation/docker_en/)
### Packages
Akkoma is packaged for [YunoHost](https://yunohost.org) and can be found and installed from the [YunoHost app catalogue](https://yunohost.org/#/apps).
### Compilation Troubleshooting ### Compilation Troubleshooting
If you ever encounter compilation issues during the updating of Akkoma, you can try these commands and see if they fix things: If you ever encounter compilation issues during the updating of Akkoma, you can try these commands and see if they fix things:

View file

@ -65,6 +65,7 @@
link_name: false, link_name: false,
proxy_remote: false, proxy_remote: false,
filename_display_max_length: 30, filename_display_max_length: 30,
default_description: nil,
base_url: nil base_url: nil
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
@ -260,8 +261,7 @@
privileged_staff: false, privileged_staff: false,
local_bubble: [], local_bubble: [],
max_frontend_settings_json_chars: 100_000, max_frontend_settings_json_chars: 100_000,
export_prometheus_metrics: true, export_prometheus_metrics: true
federated_timeline_available: true
config :pleroma, :welcome, config :pleroma, :welcome,
direct_message: [ direct_message: [
@ -745,9 +745,6 @@
primary: %{"name" => "pleroma-fe", "ref" => "stable"}, primary: %{"name" => "pleroma-fe", "ref" => "stable"},
admin: %{"name" => "admin-fe", "ref" => "stable"}, admin: %{"name" => "admin-fe", "ref" => "stable"},
mastodon: %{"name" => "mastodon-fe", "ref" => "akkoma"}, mastodon: %{"name" => "mastodon-fe", "ref" => "akkoma"},
pickable: [
"pleroma-fe/stable"
],
swagger: %{ swagger: %{
"name" => "swagger-ui", "name" => "swagger-ui",
"ref" => "stable", "ref" => "stable",
@ -813,7 +810,7 @@
private_instance? = :if_instance_is_private private_instance? = :if_instance_is_private
config :pleroma, :restrict_unauthenticated, config :pleroma, :restrict_unauthenticated,
timelines: %{local: private_instance?, federated: private_instance?, bubble: true}, timelines: %{local: private_instance?, federated: private_instance?},
profiles: %{local: private_instance?, remote: private_instance?}, profiles: %{local: private_instance?, remote: private_instance?},
activities: %{local: private_instance?, remote: private_instance?} activities: %{local: private_instance?, remote: private_instance?}

View file

@ -1,2 +0,0 @@
hehe, /emoji/hehe.png, Akkoma
nothehe, /emoji/nothehe.png, Akkoma

View file

@ -969,12 +969,6 @@
key: :export_prometheus_metrics, key: :export_prometheus_metrics,
type: :boolean, type: :boolean,
description: "Enable prometheus metrics (at /api/v1/akkoma/metrics)" description: "Enable prometheus metrics (at /api/v1/akkoma/metrics)"
},
%{
key: :federated_timeline_available,
type: :boolean,
description:
"Let people view the 'firehose' feed of all public statuses from all instances."
} }
] ]
}, },
@ -2999,11 +2993,6 @@
key: :federated, key: :federated,
type: :boolean, type: :boolean,
description: "Disallow viewing the whole known network timeline." description: "Disallow viewing the whole known network timeline."
},
%{
key: :bubble,
type: :boolean,
description: "Disallow viewing the bubble timeline."
} }
] ]
}, },
@ -3159,12 +3148,6 @@
description: description:
"A map containing available frontends and parameters for their installation.", "A map containing available frontends and parameters for their installation.",
children: frontend_options children: frontend_options
},
%{
key: :pickable,
type: {:list, :string},
description:
"A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable."
} }
] ]
}, },

View file

@ -23,7 +23,8 @@
config :pleroma, Pleroma.Upload, config :pleroma, Pleroma.Upload,
filters: [], filters: [],
link_name: false link_name: false,
default_description: :filename
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads" config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"

View file

@ -562,6 +562,7 @@ the source code is here: [kocaptcha](https://github.com/koto-bank/kocaptcha). Th
* `proxy_remote`: If you're using a remote uploader, Akkoma will proxy media requests instead of redirecting to it. * `proxy_remote`: If you're using a remote uploader, Akkoma will proxy media requests instead of redirecting to it.
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation. * `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30. * `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
* `default_description`: Sets which default description an image has if none is set explicitly. Options: nil (default) - Don't set a default, :filename - use the filename of the file, a string (e.g. "attachment") - Use this string
!!! warning !!! warning
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.

View file

@ -1,8 +1,8 @@
## Required dependencies ## Required dependencies
* PostgreSQL 9.6+ * PostgreSQL 9.6+
* Elixir 1.14+ * Elixir 1.12+ (1.13+ recommended)
* Erlang OTP 24+ * Erlang OTP 22.2+
* git * git
* file / libmagic * file / libmagic
* gcc (clang might also work) * gcc (clang might also work)

View file

@ -1,9 +0,0 @@
# Installing on Yunohost
[YunoHost](https://yunohost.org) is a server operating system aimed at self-hosting. The YunoHost community maintains a package of Akkoma which allows you to install Akkoma on YunoHost. You can install it via the normal way through the admin web interface, or through the CLI. More information can be found at [the repo of the package](https://github.com/YunoHost-Apps/akkoma_ynh).
## Questions
Questions and problems related to the YunoHost parts can be done through the [YunoHost channels](https://yunohost.org/en/help).
For questions about Akkoma, check out the [Akkoma community channels](../../#community-channels).

View file

@ -1,2 +1,2 @@
elixir_version=1.14.3 elixir_version=1.9.4
erlang_version=25.3 erlang_version=22.3.4.1

View file

@ -7,7 +7,7 @@ ExecReload=/bin/kill $MAINPID
Restart=on-failure Restart=on-failure
; Uncomment this if you're on Arch Linux ; Uncomment this if you're on Arch Linux
; Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl" ; Evironment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl"
; Name of the user that runs the Akkoma service. ; Name of the user that runs the Akkoma service.
User=akkoma User=akkoma

View file

@ -82,46 +82,4 @@ def run(["user_timeline", nickname, reading_nickname]) do
Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity) Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity)
|> IO.puts() |> IO.puts()
end end
def run(["notifications", nickname]) do
start_pleroma()
user = Repo.get_by!(User, nickname: nickname)
account_ap_id = user.ap_id
options = %{account_ap_id: user.ap_id}
query =
user
|> Pleroma.Notification.for_user_query(options)
|> where([n, a], a.actor == ^account_ap_id)
|> limit(20)
Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity)
|> IO.puts()
end
def run(["known_network", nickname]) do
start_pleroma()
user = Repo.get_by!(User, nickname: nickname)
params =
%{}
|> Map.put(:type, ["Create"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
# Restricts unfederated content to authenticated users
|> Map.put(:includes_local_public, not is_nil(user))
|> Map.put(:restrict_unlisted, true)
query =
Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query(
[Pleroma.Constants.as_public()],
params
)
|> limit(20)
Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity)
|> IO.puts()
end
end end

View file

@ -277,13 +277,6 @@ def get_create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
def get_create_by_object_ap_id_with_object(_), do: nil def get_create_by_object_ap_id_with_object(_), do: nil
def get_local_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
ap_id
|> create_by_object_ap_id()
|> where(local: true)
|> Repo.one()
end
@spec create_by_id_with_object(String.t()) :: t() | nil @spec create_by_id_with_object(String.t()) :: t() | nil
def create_by_id_with_object(id) do def create_by_id_with_object(id) do
get_by_id_with_opts(id, preload: [:object], filter: [type: "Create"]) get_by_id_with_opts(id, preload: [:object], filter: [type: "Create"])

View file

@ -21,7 +21,6 @@ defmodule Pleroma.Emoji do
:named_table, :named_table,
{:read_concurrency, true} {:read_concurrency, true}
] ]
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
defstruct [:code, :file, :tags, :safe_code, :safe_file] defstruct [:code, :file, :tags, :safe_code, :safe_file]
@ -206,7 +205,4 @@ def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified)
end end
def fully_qualify_emoji(emoji), do: emoji def fully_qualify_emoji(emoji), do: emoji
def matches_shortcode?(nil), do: false
def matches_shortcode?(s), do: Regex.match?(@emoji_regex, s)
end end

View file

@ -162,7 +162,7 @@ def local do
%Instance{ %Instance{
host: Pleroma.Web.Endpoint.host(), host: Pleroma.Web.Endpoint.host(),
favicon: Pleroma.Web.Endpoint.url() <> "/favicon.png", favicon: Pleroma.Web.Endpoint.url() <> "/favicon.png",
nodeinfo: Pleroma.Web.Nodeinfo.Nodeinfo.get_nodeinfo("2.1") nodeinfo: Pleroma.Web.Nodeinfo.NodeinfoController.raw_nodeinfo()
} }
end end

View file

@ -65,6 +65,15 @@ defmodule Pleroma.Upload do
} }
defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path] defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path]
defp get_description(opts, upload) do
case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
{description, _} when is_binary(description) -> description
{_, :filename} -> upload.name
{_, str} when is_binary(str) -> str
_ -> ""
end
end
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()} @spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
@doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct." @doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct."
def store(upload, opts \\ []) do def store(upload, opts \\ []) do
@ -73,7 +82,7 @@ def store(upload, opts \\ []) do
with {:ok, upload} <- prepare_upload(upload, opts), with {:ok, upload} <- prepare_upload(upload, opts),
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
description = Map.get(opts, :description) || "", description = get_description(opts, upload),
{_, true} <- {_, true} <-
{:description_limit, {:description_limit,
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])}, String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},

View file

@ -366,21 +366,21 @@ def invisible?(%User{invisible: true}), do: true
def invisible?(_), do: false def invisible?(_), do: false
def avatar_url(user, options \\ []) do def avatar_url(user, options \\ []) do
default = Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png") case user.avatar do
do_optional_url(user.avatar, default, options) %{"url" => [%{"href" => href} | _]} ->
end
def banner_url(user, options \\ []) do
do_optional_url(user.banner, "#{Endpoint.url()}/images/banner.png", options)
end
defp do_optional_url(field, default, options) do
case field do
%{"url" => [%{"href" => href} | _]} when is_binary(href) ->
href href
_ -> _ ->
unless options[:no_default], do: default unless options[:no_default] do
Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
end
end
end
def banner_url(user, options \\ []) do
case user.banner do
%{"url" => [%{"href" => href} | _]} -> href
_ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
end end
end end
@ -2077,14 +2077,10 @@ def parse_bio(bio, user) when is_binary(bio) and bio != "" do
# TODO: get profile URLs other than user.ap_id # TODO: get profile URLs other than user.ap_id
profile_urls = [user.ap_id] profile_urls = [user.ap_id]
CommonUtils.format_input(bio, "text/plain", bio
|> CommonUtils.format_input("text/plain",
mentions_format: :full, mentions_format: :full,
rel: fn link -> rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
case RelMe.maybe_put_rel_me(link, profile_urls) do
"me" -> "me"
_ -> nil
end
end
) )
|> elem(0) |> elem(0)
end end

View file

@ -1502,22 +1502,13 @@ def fetch_activities_bounded(
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()} @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do with {:ok, data} <- Upload.store(file, opts) do
obj_data = Maps.put_if_present(data, "actor", opts[:actor]) obj_data = Maps.put_if_present(data, "actor", opts[:actor])
Repo.insert(%Object{data: obj_data}) Repo.insert(%Object{data: obj_data})
end end
end end
defp sanitize_upload_file(%Plug.Upload{filename: filename} = upload) when is_binary(filename) do
%Plug.Upload{
upload
| filename: Path.basename(filename)
}
end
defp sanitize_upload_file(upload), do: upload
@spec get_actor_url(any()) :: binary() | nil @spec get_actor_url(any()) :: binary() | nil
defp get_actor_url(url) when is_binary(url), do: url defp get_actor_url(url) when is_binary(url), do: url
defp get_actor_url(%{"href" => href}) when is_binary(href), do: href defp get_actor_url(%{"href" => href}) when is_binary(href), do: href

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Delivery alias Pleroma.Delivery
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Fetcher
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.InternalFetchActor
@ -292,12 +293,33 @@ def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do
|> json("Invalid HTTP Signature") |> json("Invalid HTTP Signature")
end end
# POST /relay/inbox -or- POST /internal/fetch/inbox
def inbox(conn, %{"type" => "Create"} = params) do
if FederatingPlug.federating?() do
post_inbox_relayed_create(conn, params)
else
conn
|> put_status(:bad_request)
|> json("Not federating")
end
end
def inbox(conn, _params) do def inbox(conn, _params) do
conn conn
|> put_status(:bad_request) |> put_status(:bad_request)
|> json("error, missing HTTP Signature") |> json("error, missing HTTP Signature")
end end
defp post_inbox_relayed_create(conn, params) do
Logger.debug(
"Signature missing or not from author, relayed Create message, fetching object from source"
)
Fetcher.fetch_object_from_id(params["object"]["id"])
json(conn, "ok")
end
defp represent_service_actor(%User{} = user, conn) do defp represent_service_actor(%User{} = user, conn) do
conn conn
|> put_resp_content_type("application/activity+json") |> put_resp_content_type("application/activity+json")

View file

@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@primary_key false @primary_key false
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
embedded_schema do embedded_schema do
quote do quote do
@ -74,6 +75,9 @@ defp fix(data) do
end end
end end
defp matches_shortcode?(nil), do: false
defp matches_shortcode?(s), do: Regex.match?(@emoji_regex, s)
defp fix_emoji_qualification(%{"content" => emoji} = data) do defp fix_emoji_qualification(%{"content" => emoji} = data) do
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji) new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
@ -94,7 +98,7 @@ defp fix_emoji_qualification(data), do: data
defp validate_emoji(cng) do defp validate_emoji(cng) do
content = get_field(cng, :content) content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) || Emoji.matches_shortcode?(content) do if Emoji.is_unicode_emoji?(content) || matches_shortcode?(content) do
cng cng
else else
cng cng

View file

@ -108,28 +108,15 @@ defp blocked_instances do
Config.get([:mrf_simple, :reject], []) Config.get([:mrf_simple, :reject], [])
end end
defp allowed_instances do
Config.get([:mrf_simple, :accept])
end
def should_federate?(url) do def should_federate?(url) do
%{host: host} = URI.parse(url) %{host: host} = URI.parse(url)
with allowed <- allowed_instances(),
false <- Enum.empty?(allowed) do
allowed
|> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
|> Pleroma.Web.ActivityPub.MRF.subdomain_match?(host)
else
_ ->
quarantined_instances = quarantined_instances =
blocked_instances() blocked_instances()
|> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex() |> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
not Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host) !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
end
end end
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | [] @spec recipients(User.t(), Activity.t()) :: list(User.t()) | []

View file

@ -419,19 +419,28 @@ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id
def handle_incoming( def handle_incoming(
%{ %{
"type" => "Like", "type" => "Like",
"content" => reaction "_misskey_reaction" => reaction,
"tag" => _
} = data, } = data,
options options
) do ) do
if Pleroma.Emoji.is_unicode_emoji?(reaction) || Pleroma.Emoji.matches_shortcode?(reaction) do
data data
|> Map.put("type", "EmojiReact") |> Map.put("type", "EmojiReact")
|> handle_incoming(options) |> Map.put("content", reaction)
else
data
|> Map.delete("content")
|> handle_incoming(options) |> handle_incoming(options)
end end
def handle_incoming(
%{
"type" => "Like",
"_misskey_reaction" => reaction
} = data,
options
) do
data
|> Map.put("type", "EmojiReact")
|> Map.put("content", reaction)
|> handle_incoming(options)
end end
def handle_incoming( def handle_incoming(

View file

@ -5,16 +5,6 @@ defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do
alias Pleroma.Akkoma.FrontendSettingsProfile alias Pleroma.Akkoma.FrontendSettingsProfile
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
plug(
OAuthScopesPlug,
@unauthenticated_access
when action in [
:available_frontends,
:update_preferred_frontend
]
)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{@unauthenticated_access | scopes: ["read:accounts"]} %{@unauthenticated_access | scopes: ["read:accounts"]}
@ -103,22 +93,4 @@ def update_profile(%{body_params: %{settings: settings, version: version}} = con
|> json(profile.settings) |> json(profile.settings)
end end
end end
@doc "GET /api/v1/akkoma/preferred_frontend/available"
def available_frontends(conn, _params) do
available = Pleroma.Config.get([:frontends, :pickable])
conn
|> json(available)
end
@doc "PUT /api/v1/akkoma/preferred_frontend"
def update_preferred_frontend(
%{body_params: %{frontend_name: preferred_frontend}} = conn,
_params
) do
conn
|> put_resp_cookie("preferred_frontend", preferred_frontend)
|> json(%{frontend_name: preferred_frontend})
end
end end

View file

@ -1,20 +0,0 @@
defmodule Pleroma.Web.AkkomaAPI.FrontendSwitcherController do
use Pleroma.Web, :controller
alias Pleroma.Config
@doc "GET /akkoma/frontend"
def switch(conn, _params) do
pickable = Config.get([:frontends, :pickable], [])
conn
|> put_view(Pleroma.Web.AkkomaAPI.FrontendSwitcherView)
|> render("switch.html", choices: pickable)
end
@doc "POST /akkoma/frontend"
def do_switch(conn, params) do
conn
|> put_resp_cookie("preferred_frontend", params["frontend"])
|> html("<meta http-equiv=\"refresh\" content=\"0; url=/\">")
end
end

View file

@ -1,3 +0,0 @@
defmodule Pleroma.Web.AkkomaAPI.FrontendSwitcherView do
use Pleroma.Web, :view
end

View file

@ -225,12 +225,6 @@ defp update_request do
type: :integer, type: :integer,
description: description:
"Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire." "Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire."
},
expires_at: %Schema{
nullable: true,
type: :string,
description:
"When the filter should no longer be applied. String (ISO 8601 Datetime), or null if the filter does not expire."
} }
}, },
required: [:phrase, :context], required: [:phrase, :context],

View file

@ -12,7 +12,7 @@ def open_api_operation(action) do
@spec list_profiles_operation() :: Operation.t() @spec list_profiles_operation() :: Operation.t()
def list_profiles_operation() do def list_profiles_operation() do
%Operation{ %Operation{
tags: ["Frontends"], tags: ["Retrieve frontend setting profiles"],
summary: "Frontend Settings Profiles", summary: "Frontend Settings Profiles",
description: "List frontend setting profiles", description: "List frontend setting profiles",
operationId: "AkkomaAPI.FrontendSettingsController.list_profiles", operationId: "AkkomaAPI.FrontendSettingsController.list_profiles",
@ -37,7 +37,7 @@ def list_profiles_operation() do
@spec get_profile_operation() :: Operation.t() @spec get_profile_operation() :: Operation.t()
def get_profile_operation() do def get_profile_operation() do
%Operation{ %Operation{
tags: ["Frontends"], tags: ["Retrieve frontend setting profile"],
summary: "Frontend Settings Profile", summary: "Frontend Settings Profile",
description: "Get frontend setting profile", description: "Get frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.get_profile", operationId: "AkkomaAPI.FrontendSettingsController.get_profile",
@ -60,7 +60,7 @@ def get_profile_operation() do
@spec delete_profile_operation() :: Operation.t() @spec delete_profile_operation() :: Operation.t()
def delete_profile_operation() do def delete_profile_operation() do
%Operation{ %Operation{
tags: ["Frontends"], tags: ["Delete frontend setting profile"],
summary: "Delete frontend Settings Profile", summary: "Delete frontend Settings Profile",
description: "Delete frontend setting profile", description: "Delete frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.delete_profile", operationId: "AkkomaAPI.FrontendSettingsController.delete_profile",
@ -76,7 +76,7 @@ def delete_profile_operation() do
@spec update_profile_operation() :: Operation.t() @spec update_profile_operation() :: Operation.t()
def update_profile_operation() do def update_profile_operation() do
%Operation{ %Operation{
tags: ["Frontends"], tags: ["Update frontend setting profile"],
summary: "Frontend Settings Profile", summary: "Frontend Settings Profile",
description: "Update frontend setting profile", description: "Update frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation", operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation",
@ -90,57 +90,6 @@ def update_profile_operation() do
} }
end end
def available_frontends_operation() do
%Operation{
tags: ["Frontends"],
summary: "Frontend Settings Profiles",
description: "List frontend setting profiles",
operationId: "AkkomaAPI.FrontendSettingsController.available_frontends",
responses: %{
200 =>
Operation.response("Frontends", "application/json", %Schema{
type: :array,
items: %Schema{
type: :string
}
})
}
}
end
def update_preferred_frontend_operation() do
%Operation{
tags: ["Frontends"],
summary: "Frontend Settings Profiles",
description: "List frontend setting profiles",
operationId: "AkkomaAPI.FrontendSettingsController.available_frontends",
requestBody:
request_body(
"Frontend",
%Schema{
type: :object,
required: [:frontend_name],
properties: %{
frontend_name: %Schema{
type: :string,
description: "Frontend name"
}
}
},
required: true
),
responses: %{
200 =>
Operation.response("Frontends", "application/json", %Schema{
type: :array,
items: %Schema{
type: :string
}
})
}
}
end
def frontend_name_param do def frontend_name_param do
Operation.parameter(:frontend_name, :path, :string, "Frontend name", Operation.parameter(:frontend_name, :path, :string, "Frontend name",
example: "pleroma-fe", example: "pleroma-fe",

View file

@ -70,8 +70,7 @@ def public_operation do
operationId: "TimelineController.public", operationId: "TimelineController.public",
responses: %{ responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses()), 200 => Operation.response("Array of Status", "application/json", array_of_statuses()),
401 => Operation.response("Error", "application/json", ApiError), 401 => Operation.response("Error", "application/json", ApiError)
404 => Operation.response("Error", "application/json", ApiError)
} }
} }
end end

View file

@ -20,7 +20,7 @@ def api_not_implemented(conn, _params) do
def redirector(conn, _params, code \\ 200) do def redirector(conn, _params, code \\ 200) do
conn conn
|> put_resp_content_type("text/html") |> put_resp_content_type("text/html")
|> send_file(code, index_file_path(conn)) |> send_file(code, index_file_path())
end end
def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
@ -33,7 +33,7 @@ def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id}
end end
def redirector_with_meta(conn, params) do def redirector_with_meta(conn, params) do
{:ok, index_content} = File.read(index_file_path(conn)) {:ok, index_content} = File.read(index_file_path())
tags = build_tags(conn, params) tags = build_tags(conn, params)
preloads = preload_data(conn, params) preloads = preload_data(conn, params)
@ -53,7 +53,7 @@ def redirector_with_preload(conn, %{"path" => ["pleroma", "admin"]}) do
end end
def redirector_with_preload(conn, params) do def redirector_with_preload(conn, params) do
{:ok, index_content} = File.read(index_file_path(conn)) {:ok, index_content} = File.read(index_file_path())
preloads = preload_data(conn, params) preloads = preload_data(conn, params)
tags = Metadata.build_static_tags(params) tags = Metadata.build_static_tags(params)
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>" title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
@ -77,9 +77,8 @@ def empty(conn, _params) do
|> text("") |> text("")
end end
defp index_file_path(conn) do defp index_file_path do
frontend_type = Pleroma.Web.Plugs.FrontendStatic.preferred_or_fallback(conn, :primary) Pleroma.Web.Plugs.InstanceStatic.file_path("index.html")
Pleroma.Web.Plugs.InstanceStatic.file_path("index.html", frontend_type)
end end
defp build_tags(conn, params) do defp build_tags(conn, params) do

View file

@ -16,7 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
alias Pleroma.Web.Plugs.RateLimiter alias Pleroma.Web.Plugs.RateLimiter
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:skip_public_check when action in [:public, :hashtag, :bubble]) plug(:skip_public_check when action in [:public, :hashtag])
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it: # TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e # https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
@ -28,25 +28,19 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list) plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list)
plug(RateLimiter, [name: :timeline, bucket_name: :bubble_timeline] when action == :bubble) plug(RateLimiter, [name: :timeline, bucket_name: :bubble_timeline] when action == :bubble)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct, :bubble])
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
when action in [:public, :hashtag, :bubble] when action in [:public, :hashtag]
) )
require Logger
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
# GET /api/v1/timelines/home # GET /api/v1/timelines/home
def home(%{assigns: %{user: user}} = conn, params) do def home(%{assigns: %{user: user}} = conn, params) do
%{nickname: nickname} = user
Logger.debug("TimelineController.home: #{nickname}")
followed_hashtags = followed_hashtags =
user user
|> User.followed_hashtags() |> User.followed_hashtags()
@ -64,15 +58,11 @@ def home(%{assigns: %{user: user}} = conn, params) do
|> Map.put(:followed_hashtags, followed_hashtags) |> Map.put(:followed_hashtags, followed_hashtags)
|> Map.delete(:local) |> Map.delete(:local)
Logger.debug("TimelineController.home: #{nickname} - fetching activities")
activities = activities =
[user.ap_id | User.following(user)] [user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params) |> ActivityPub.fetch_activities(params)
|> Enum.reverse() |> Enum.reverse()
Logger.debug("TimelineController.home: #{nickname} - rendering")
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", |> render("index.json",
@ -85,8 +75,6 @@ def home(%{assigns: %{user: user}} = conn, params) do
# GET /api/v1/timelines/direct # GET /api/v1/timelines/direct
def direct(%{assigns: %{user: user}} = conn, params) do def direct(%{assigns: %{user: user}} = conn, params) do
Logger.debug("TimelineController.direct: #{user.nickname}")
params = params =
params params
|> Map.put(:type, "Create") |> Map.put(:type, "Create")
@ -94,15 +82,11 @@ def direct(%{assigns: %{user: user}} = conn, params) do
|> Map.put(:user, user) |> Map.put(:user, user)
|> Map.put(:visibility, "direct") |> Map.put(:visibility, "direct")
Logger.debug("TimelineController.direct: #{user.nickname} - fetching activities")
activities = activities =
[user.ap_id] [user.ap_id]
|> ActivityPub.fetch_activities_query(params) |> ActivityPub.fetch_activities_query(params)
|> Pagination.fetch_paginated(params) |> Pagination.fetch_paginated(params)
Logger.debug("TimelineController.direct: #{user.nickname} - rendering")
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", |> render("index.json",
@ -112,22 +96,21 @@ def direct(%{assigns: %{user: user}} = conn, params) do
) )
end end
defp restrict_unauthenticated?(type) do defp restrict_unauthenticated?(true = _local_only) do
Config.restrict_unauthenticated_access?(:timelines, type) Config.restrict_unauthenticated_access?(:timelines, :local)
end
defp restrict_unauthenticated?(_) do
Config.restrict_unauthenticated_access?(:timelines, :federated)
end end
# GET /api/v1/timelines/public # GET /api/v1/timelines/public
def public(%{assigns: %{user: user}} = conn, params) do def public(%{assigns: %{user: user}} = conn, params) do
Logger.debug("TimelineController.public")
local_only = params[:local] local_only = params[:local]
timeline_type = if local_only, do: :local, else: :federated
with {:enabled, true} <-
{:enabled, local_only || Config.get([:instance, :federated_timeline_available], true)},
{:authenticated, true} <-
{:authenticated, !(is_nil(user) and restrict_unauthenticated?(timeline_type))} do
Logger.debug("TimelineController.public: fetching activities")
if is_nil(user) and restrict_unauthenticated?(local_only) do
fail_on_bad_auth(conn)
else
activities = activities =
params params
|> Map.put(:type, ["Create"]) |> Map.put(:type, ["Create"])
@ -140,8 +123,6 @@ def public(%{assigns: %{user: user}} = conn, params) do
|> Map.put(:includes_local_public, not is_nil(user)) |> Map.put(:includes_local_public, not is_nil(user))
|> ActivityPub.fetch_public_activities() |> ActivityPub.fetch_public_activities()
Logger.debug("TimelineController.public: rendering")
conn conn
|> add_link_headers(activities, %{"local" => local_only}) |> add_link_headers(activities, %{"local" => local_only})
|> render("index.json", |> render("index.json",
@ -150,32 +131,20 @@ def public(%{assigns: %{user: user}} = conn, params) do
as: :activity, as: :activity,
with_muted: Map.get(params, :with_muted, false) with_muted: Map.get(params, :with_muted, false)
) )
else
{:enabled, false} ->
conn
|> put_status(404)
|> json(%{error: "Federated timeline is disabled"})
{:authenticated, false} ->
fail_on_bad_auth(conn)
end end
end end
# GET /api/v1/timelines/bubble # GET /api/v1/timelines/bubble
def bubble(%{assigns: %{user: user}} = conn, params) do def bubble(%{assigns: %{user: user}} = conn, params) do
Logger.debug("TimelineController.bubble")
if is_nil(user) and restrict_unauthenticated?(:bubble) do
fail_on_bad_auth(conn)
else
bubble_instances = bubble_instances =
Enum.uniq( Enum.uniq(
Config.get([:instance, :local_bubble], []) ++ Config.get([:instance, :local_bubble], []) ++
[Pleroma.Web.Endpoint.host()] [Pleroma.Web.Endpoint.host()]
) )
Logger.debug("TimelineController.bubble: fetching activities") if is_nil(user) do
fail_on_bad_auth(conn)
else
activities = activities =
params params
|> Map.put(:type, ["Create"]) |> Map.put(:type, ["Create"])
@ -185,8 +154,6 @@ def bubble(%{assigns: %{user: user}} = conn, params) do
|> Map.put(:instance, bubble_instances) |> Map.put(:instance, bubble_instances)
|> ActivityPub.fetch_public_activities() |> ActivityPub.fetch_public_activities()
Logger.debug("TimelineController.bubble: rendering")
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", |> render("index.json",
@ -228,7 +195,7 @@ defp hashtag_fetching(params, user, local_only) do
def hashtag(%{assigns: %{user: user}} = conn, params) do def hashtag(%{assigns: %{user: user}} = conn, params) do
local_only = params[:local] local_only = params[:local]
if is_nil(user) and restrict_unauthenticated?(if local_only, do: :local, else: :federated) do if is_nil(user) and restrict_unauthenticated?(local_only) do
fail_on_bad_auth(conn) fail_on_bad_auth(conn)
else else
activities = hashtag_fetching(params, user, local_only) activities = hashtag_fetching(params, user, local_only)

View file

@ -65,11 +65,7 @@ def features do
"shareable_emoji_packs", "shareable_emoji_packs",
"multifetch", "multifetch",
"pleroma:api/v1/notifications:include_types_filter", "pleroma:api/v1/notifications:include_types_filter",
"quote_posting",
"editing", "editing",
if !Enum.empty?(Config.get([:instance, :local_bubble], [])) do
"bubble_timeline"
end,
if Config.get([:media_proxy, :enabled]) do if Config.get([:media_proxy, :enabled]) do
"media_proxy" "media_proxy"
end, end,

View file

@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
alias Pleroma.Web.PleromaAPI.EmojiReactionController alias Pleroma.Web.PleromaAPI.EmojiReactionController
require Logger
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2] import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2]
@ -88,7 +87,6 @@ defp reblogged?(activity, %User{ap_id: ap_id}) do
defp reblogged?(_activity, _user), do: false defp reblogged?(_activity, _user), do: false
def render("index.json", opts) do def render("index.json", opts) do
Logger.debug("Rendering index")
reading_user = opts[:for] reading_user = opts[:for]
# To do: check AdminAPIControllerTest on the reasons behind nil activities in the list # To do: check AdminAPIControllerTest on the reasons behind nil activities in the list
activities = Enum.filter(opts.activities, & &1) activities = Enum.filter(opts.activities, & &1)
@ -138,10 +136,8 @@ def render("index.json", opts) do
def render( def render(
"show.json", "show.json",
%{activity: %{id: id, data: %{"type" => "Announce", "object" => _object}} = activity} = %{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts
opts
) do ) do
Logger.debug("Rendering reblog #{id}")
user = CommonAPI.get_user(activity.data["actor"]) user = CommonAPI.get_user(activity.data["actor"])
created_at = Utils.to_masto_date(activity.data["published"]) created_at = Utils.to_masto_date(activity.data["published"])
object = Object.normalize(activity, fetch: false) object = Object.normalize(activity, fetch: false)
@ -213,9 +209,7 @@ def render(
} }
end end
def render("show.json", %{activity: %{id: id, data: %{"object" => _object}} = activity} = opts) do def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
Logger.debug("Rendering status #{id}")
with %Object{} = object <- Object.normalize(activity, fetch: false) do with %Object{} = object <- Object.normalize(activity, fetch: false) do
user = CommonAPI.get_user(activity.data["actor"]) user = CommonAPI.get_user(activity.data["actor"])
user_follower_address = user.follower_address user_follower_address = user.follower_address
@ -233,10 +227,8 @@ def render("show.json", %{activity: %{id: id, data: %{"object" => _object}} = ac
|> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end) |> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end)
|> Enum.map(fn tag -> tag["href"] end) |> Enum.map(fn tag -> tag["href"] end)
to_data = if is_nil(object.data["to"]), do: [], else: object.data["to"]
mentions = mentions =
(to_data ++ tag_mentions) (object.data["to"] ++ tag_mentions)
|> Enum.uniq() |> Enum.uniq()
|> Enum.map(fn |> Enum.map(fn
Pleroma.Constants.as_public() -> nil Pleroma.Constants.as_public() -> nil
@ -434,7 +426,6 @@ def render("show.json", _) do
end end
def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
Logger.debug("Rendering history for #{activity.id}")
object = Object.normalize(activity, fetch: false) object = Object.normalize(activity, fetch: false)
hashtags = Object.hashtags(object) hashtags = Object.hashtags(object)
@ -621,8 +612,6 @@ def render("attachment_meta.json", %{
def render("attachment_meta.json", _), do: nil def render("attachment_meta.json", _), do: nil
def render("context.json", %{activity: activity, activities: activities, user: user}) do def render("context.json", %{activity: activity, activities: activities, user: user}) do
Logger.debug("Rendering context for #{activity.id}")
%{ancestors: ancestors, descendants: descendants} = %{ancestors: ancestors, descendants: descendants} =
activities activities
|> Enum.reverse() |> Enum.reverse()

View file

@ -71,15 +71,7 @@ def get_nodeinfo("2.0") do
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]), restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false), skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
privilegedStaff: Config.get([:instance, :privileged_staff]), privilegedStaff: Config.get([:instance, :privileged_staff]),
localBubbleInstances: Config.get([:instance, :local_bubble], []), localBubbleInstances: Config.get([:instance, :local_bubble], [])
publicTimelineVisibility: %{
federated:
!Config.restrict_unauthenticated_access?(:timelines, :federated) &&
Config.get([:instance, :federated_timeline_available], true),
local: !Config.restrict_unauthenticated_access?(:timelines, :local),
bubble: !Config.restrict_unauthenticated_access?(:timelines, :bubble)
},
federatedTimelineAvailable: Config.get([:instance, :federated_timeline_available], true)
} }
} }
end end

View file

@ -5,8 +5,12 @@
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Config
alias Pleroma.Stats
alias Pleroma.User
alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.MastodonAPI.InstanceView
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
alias Pleroma.Web.Nodeinfo.Nodeinfo
def schemas(conn, _params) do def schemas(conn, _params) do
response = %{ response = %{
@ -25,15 +29,101 @@ def schemas(conn, _params) do
json(conn, response) json(conn, response)
end end
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
# under software.
def raw_nodeinfo do
stats = Stats.get_stats()
staff_accounts =
User.all_superusers()
|> Enum.map(fn u -> u.ap_id end)
|> Enum.filter(fn u -> not Enum.member?(Config.get([:instance, :staff_transparency]), u) end)
features = InstanceView.features()
federation = InstanceView.federation()
%{
version: "2.0",
software: %{
name: Pleroma.Application.name() |> String.downcase(),
version: Pleroma.Application.version()
},
protocols: Publisher.gather_nodeinfo_protocol_names(),
services: %{
inbound: [],
outbound: []
},
openRegistrations: Config.get([:instance, :registrations_open]),
usage: %{
users: %{
total: Map.get(stats, :user_count, 0)
},
localPosts: Map.get(stats, :status_count, 0)
},
metadata: %{
nodeName: Config.get([:instance, :name]),
nodeDescription: Config.get([:instance, :description]),
private: !Config.get([:instance, :public], true),
suggestions: %{
enabled: false
},
staffAccounts: staff_accounts,
federation: federation,
pollLimits: Config.get([:instance, :poll_limits]),
postFormats: Config.get([:instance, :allowed_post_formats]),
uploadLimits: %{
general: Config.get([:instance, :upload_limit]),
avatar: Config.get([:instance, :avatar_upload_limit]),
banner: Config.get([:instance, :banner_upload_limit]),
background: Config.get([:instance, :background_upload_limit])
},
fieldsLimits: %{
maxFields: Config.get([:instance, :max_account_fields]),
maxRemoteFields: Config.get([:instance, :max_remote_account_fields]),
nameLength: Config.get([:instance, :account_field_name_length]),
valueLength: Config.get([:instance, :account_field_value_length])
},
accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Config.get([:instance, :invites_enabled], false),
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
localBubbleInstances: Config.get([:instance, :local_bubble], [])
}
}
end
# Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json # Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json
# and https://github.com/jhass/nodeinfo/blob/master/schemas/2.1/schema.json # and https://github.com/jhass/nodeinfo/blob/master/schemas/2.1/schema.json
def nodeinfo(conn, %{"version" => version}) when version in ["2.0", "2.1"] do def nodeinfo(conn, %{"version" => "2.0"}) do
conn conn
|> put_resp_header( |> put_resp_header(
"content-type", "content-type",
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8" "application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
) )
|> json(Nodeinfo.get_nodeinfo(version)) |> json(raw_nodeinfo())
end
def nodeinfo(conn, %{"version" => "2.1"}) do
raw_response = raw_nodeinfo()
updated_software =
raw_response
|> Map.get(:software)
|> Map.put(:repository, Pleroma.Application.repository())
response =
raw_response
|> Map.put(:software, updated_software)
|> Map.put(:version, "2.1")
conn
|> put_resp_header(
"content-type",
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.1#; charset=utf-8"
)
|> json(response)
end end
def nodeinfo(conn, _) do def nodeinfo(conn, _) do

View file

@ -71,8 +71,6 @@ def validate(scopes, app_scopes, _user) do
""" """
def filter_admin_scopes(scopes, %Pleroma.User{is_admin: true}), do: scopes def filter_admin_scopes(scopes, %Pleroma.User{is_admin: true}), do: scopes
def filter_admin_scopes(scopes, %Pleroma.User{is_moderator: true}), do: scopes
def filter_admin_scopes(scopes, _user) do def filter_admin_scopes(scopes, _user) do
drop_scopes = OAuthScopesPlug.filter_descendants(scopes, ["admin"]) drop_scopes = OAuthScopesPlug.filter_descendants(scopes, ["admin"])
Enum.reject(scopes, fn scope -> Enum.member?(drop_scopes, scope) end) Enum.reject(scopes, fn scope -> Enum.member?(drop_scopes, scope) end)

View file

@ -36,7 +36,7 @@ def object(%{assigns: %{format: format}} = conn, _params)
def object(conn, _params) do def object(conn, _params) do
with id <- Endpoint.url() <> conn.request_path, with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {_, %Activity{} = activity} <-
{:activity, Activity.get_local_create_by_object_ap_id(id)}, {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)}, {_, true} <- {:public?, Visibility.is_public?(activity)},
{_, false} <- {:local_public?, Visibility.is_local_public?(activity)} do {_, false} <- {:local_public?, Visibility.is_local_public?(activity)} do
redirect(conn, to: "/notice/#{activity.id}") redirect(conn, to: "/notice/#{activity.id}")

View file

@ -5,23 +5,17 @@
defmodule Pleroma.Web.Plugs.FrontendStatic do defmodule Pleroma.Web.Plugs.FrontendStatic do
require Pleroma.Constants require Pleroma.Constants
@frontend_cookie_name "preferred_frontend"
@moduledoc """ @moduledoc """
This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends. This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends.
""" """
@behaviour Plug @behaviour Plug
defp instance_static_path do def file_path(path, frontend_type \\ :primary) do
Pleroma.Config.get([:instance, :static_dir], "instance/static")
end
def file_path(path, frontend_type \\ :primary)
def file_path(path, frontend_type) when is_atom(frontend_type) do
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
Path.join([ Path.join([
instance_static_path(), instance_static_path,
"frontends", "frontends",
configuration["name"], configuration["name"],
configuration["ref"], configuration["ref"],
@ -32,15 +26,6 @@ def file_path(path, frontend_type) when is_atom(frontend_type) do
end end
end end
def file_path(path, frontend_type) when is_binary(frontend_type) do
Path.join([
instance_static_path(),
"frontends",
frontend_type,
path
])
end
def init(opts) do def init(opts) do
opts opts
|> Keyword.put(:from, "__unconfigured_frontend_static_plug") |> Keyword.put(:from, "__unconfigured_frontend_static_plug")
@ -53,8 +38,7 @@ def call(conn, opts) do
with false <- api_route?(conn.path_info), with false <- api_route?(conn.path_info),
false <- invalid_path?(conn.path_info), false <- invalid_path?(conn.path_info),
true <- enabled?(opts[:if]), true <- enabled?(opts[:if]),
fallback_frontend_type <- Map.get(opts, :frontend_type, :primary), frontend_type <- Map.get(opts, :frontend_type, :primary),
frontend_type <- preferred_or_fallback(conn, fallback_frontend_type),
path when not is_nil(path) <- file_path("", frontend_type) do path when not is_nil(path) <- file_path("", frontend_type) do
call_static(conn, opts, path) call_static(conn, opts, path)
else else
@ -63,31 +47,6 @@ def call(conn, opts) do
end end
end end
def preferred_frontend(conn) do
%{req_cookies: cookies} =
conn
|> Plug.Conn.fetch_cookies()
Map.get(cookies, @frontend_cookie_name)
end
# Only override primary frontend
def preferred_or_fallback(conn, :primary) do
case preferred_frontend(conn) do
nil ->
:primary
frontend ->
if Enum.member?(Pleroma.Config.get([:frontends, :pickable], []), frontend) do
frontend
else
:primary
end
end
end
def preferred_or_fallback(_conn, fallback), do: fallback
defp enabled?(if_opt) when is_function(if_opt), do: if_opt.() defp enabled?(if_opt) when is_function(if_opt), do: if_opt.()
defp enabled?(true), do: true defp enabled?(true), do: true
defp enabled?(_), do: false defp enabled?(_), do: false

View file

@ -8,8 +8,6 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
require Logger require Logger
@mix_env Mix.env()
def init(opts), do: opts def init(opts), do: opts
def call(conn, _options) do def call(conn, _options) do
@ -118,13 +116,6 @@ defp csp_string(conn) do
script_src = "script-src 'self' 'unsafe-eval' '#{nonce_tag}'" script_src = "script-src 'self' 'unsafe-eval' '#{nonce_tag}'"
script_src =
if @mix_env == :dev do
"script-src 'self' 'unsafe-eval' 'unsafe-inline'"
else
script_src
end
report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
insecure = if scheme == "https", do: "upgrade-insecure-requests" insecure = if scheme == "https", do: "upgrade-insecure-requests"

View file

@ -12,11 +12,11 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
""" """
@behaviour Plug @behaviour Plug
def file_path(path, frontend_type \\ :primary) do def file_path(path) do
instance_path = instance_path =
Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path) Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path)
frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, frontend_type) frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, :primary)
(File.exists?(instance_path) && instance_path) || (File.exists?(instance_path) && instance_path) ||
(frontend_path && File.exists?(frontend_path) && frontend_path) || (frontend_path && File.exists?(frontend_path) && frontend_path) ||

View file

@ -37,18 +37,15 @@ defp parse_url(url) do
end end
def maybe_put_rel_me("http" <> _ = target_page, profile_urls) when is_list(profile_urls) do def maybe_put_rel_me("http" <> _ = target_page, profile_urls) when is_list(profile_urls) do
with {:parse, {:ok, rel_me_hrefs}} <- {:parse, parse(target_page)}, {:ok, rel_me_hrefs} = parse(target_page)
{:link_match, true} <- true = Enum.any?(rel_me_hrefs, fn x -> x in profile_urls end)
{:link_match, Enum.any?(rel_me_hrefs, fn x -> x in profile_urls end)} do
"me" "me"
else
e -> {:error, {:could_not_verify, target_page, e}}
end
rescue rescue
e -> {:error, {:could_not_fetch, target_page, e}} _ -> nil
end end
def maybe_put_rel_me(_, _) do def maybe_put_rel_me(_, _) do
{:error, :invalid_url} nil
end end
end end

View file

@ -466,29 +466,6 @@ defmodule Pleroma.Web.Router do
put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create) put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create)
end end
scope "/akkoma/", Pleroma.Web.AkkomaAPI do
pipe_through(:browser)
get("/frontend", FrontendSwitcherController, :switch)
post("/frontend", FrontendSwitcherController, :do_switch)
end
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
pipe_through(:api)
get(
"/api/v1/akkoma/preferred_frontend/available",
FrontendSettingsController,
:available_frontends
)
put(
"/api/v1/akkoma/preferred_frontend",
FrontendSettingsController,
:update_preferred_frontend
)
end
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
pipe_through(:authenticated_api) pipe_through(:authenticated_api)
get("/metrics", MetricsController, :show) get("/metrics", MetricsController, :show)
@ -621,6 +598,7 @@ defmodule Pleroma.Web.Router do
get("/timelines/home", TimelineController, :home) get("/timelines/home", TimelineController, :home)
get("/timelines/direct", TimelineController, :direct) get("/timelines/direct", TimelineController, :direct)
get("/timelines/list/:list_id", TimelineController, :list) get("/timelines/list/:list_id", TimelineController, :list)
get("/timelines/bubble", TimelineController, :bubble)
get("/announcements", AnnouncementController, :index) get("/announcements", AnnouncementController, :index)
post("/announcements/:id/dismiss", AnnouncementController, :mark_read) post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
@ -675,7 +653,6 @@ defmodule Pleroma.Web.Router do
get("/timelines/public", TimelineController, :public) get("/timelines/public", TimelineController, :public)
get("/timelines/tag/:tag", TimelineController, :hashtag) get("/timelines/tag/:tag", TimelineController, :hashtag)
get("/timelines/bubble", TimelineController, :bubble)
get("/polls/:id", PollController, :show) get("/polls/:id", PollController, :show)

View file

@ -1,10 +0,0 @@
<h2>Switch Frontend</h2>
<h3>After you submit, you will need to refresh manually to get your new frontend!</h3>
<%= form_for @conn, Routes.frontend_switcher_path(@conn, :do_switch), fn f -> %>
<%= select(f, :frontend, @choices) %>
<%= submit do: "submit" %>
<% end %>

View file

@ -19,7 +19,6 @@
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/getting_started.js'> <link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/getting_started.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/compose.js'> <link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/compose.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/home_timeline.js'> <link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/home_timeline.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/public_timeline.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/notifications.js'> <link rel='preload' as='script' crossorigin='anonymous' href='/packs/js/features/notifications.js'>
<script crossorigin='anonymous' src="/packs/js/application.js"></script> <script crossorigin='anonymous' src="/packs/js/application.js"></script>

View file

@ -4,8 +4,8 @@ defmodule Pleroma.Mixfile do
def project do def project do
[ [
app: :pleroma, app: :pleroma,
version: version("3.8.0"), version: version("3.6.0"),
elixir: "~> 1.14", elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix] ++ Mix.compilers(), compilers: [:phoenix] ++ Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors()], elixirc_options: [warnings_as_errors: warnings_as_errors()],

View file

@ -5,10 +5,10 @@
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"}, "cachex": {:hex, :cachex, "3.5.0", "f715390a9e93125980187dcd7c4036ece92d273fbd9ec009a8ffa480abdc51f8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "fac2ebfa200dd9ffba08cdcef404426ccadfcb92281ca34f810535712d02b049"},
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"}, "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "castore": {:hex, :castore, "0.1.20", "62a0126cbb7cb3e259257827b9190f88316eb7aa3fdac01fd6f2dfd64e7f46e9", [:mix], [], "hexpm", "a020b7650529c986c454a4035b6b13a328e288466986307bea3aadb4c95ac98a"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
@ -24,10 +24,10 @@
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"}, "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark": {:hex, :earmark, "1.4.37", "56ce845c543393aa3f9b294c818c3d783452a4a67e4ab18c4303a954a8b59363", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "d86d5e12868db86d5321b00e62a4bbcb4150346e4acc9a90a041fb188a5cb106"}, "earmark": {:hex, :earmark, "1.4.34", "d7f89d3bbd7567a0bffc465e0a949f8f8dcbe43909c3acf96f4761a302cea10c", [:mix], [{:earmark_parser, "~> 1.4.29", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "90b106f3dad85b133b10d7d628167c88246123fd1cecb4557d83d21ec9e65504"},
"earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"}, "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"}, "eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"},
"ecto": {:hex, :ecto, "3.9.5", "9f0aa7ae44a1577b651c98791c6988cd1b69b21bc724e3fd67090b97f7604263", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d4f3115d8cbacdc0bfa4b742865459fb1371d0715515842a1fb17fe31920b74c"}, "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"}, "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"}, "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
@ -38,7 +38,7 @@
"ex_aws": {:hex, :ex_aws, "2.1.9", "dc4865ecc20a05190a34a0ac5213e3e5e2b0a75a0c2835e923ae7bfeac5e3c31", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "3e6c776703c9076001fbe1f7c049535f042cb2afa0d2cbd3b47cbc4e92ac0d10"}, "ex_aws": {:hex, :ex_aws, "2.1.9", "dc4865ecc20a05190a34a0ac5213e3e5e2b0a75a0c2835e923ae7bfeac5e3c31", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "3e6c776703c9076001fbe1f7c049535f042cb2afa0d2cbd3b47cbc4e92ac0d10"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.4.0", "ce8decb6b523381812798396bc0e3aaa62282e1b40520125d1f4eff4abdff0f4", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "85dda6e27754d94582869d39cba3241d9ea60b6aa4167f9c88e309dc687e56bb"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.4.0", "ce8decb6b523381812798396bc0e3aaa62282e1b40520125d1f4eff4abdff0f4", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "85dda6e27754d94582869d39cba3241d9ea60b6aa4167f9c88e309dc687e56bb"},
"ex_const": {:hex, :ex_const, "0.2.4", "d06e540c9d834865b012a17407761455efa71d0ce91e5831e86881b9c9d82448", [:mix], [], "hexpm", "96fd346610cc992b8f896ed26a98be82ac4efb065a0578f334a32d60a3ba9767"}, "ex_const": {:hex, :ex_const, "0.2.4", "d06e540c9d834865b012a17407761455efa71d0ce91e5831e86881b9c9d82448", [:mix], [], "hexpm", "96fd346610cc992b8f896ed26a98be82ac4efb065a0578f334a32d60a3ba9767"},
"ex_doc": {:hex, :ex_doc, "0.29.3", "f07444bcafb302db86e4f02d8bbcd82f2e881a0dcf4f3e4740e4b8128b9353f7", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3dc6787d7b08801ec3b51e9bd26be5e8826fbf1a17e92d1ebc252e1a1c75bfe1"}, "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"}, "ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
"excoveralls": {:hex, :excoveralls, "0.15.1", "83c8cf7973dd9d1d853dce37a2fb98aaf29b564bf7d01866e409abf59dac2c0e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8416bd90c0082d56a2178cf46c837595a06575f70a5624f164a1ffe37de07e7"}, "excoveralls": {:hex, :excoveralls, "0.15.1", "83c8cf7973dd9d1d853dce37a2fb98aaf29b564bf7d01866e409abf59dac2c0e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8416bd90c0082d56a2178cf46c837595a06575f70a5624f164a1ffe37de07e7"},
@ -47,7 +47,7 @@
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"}, "finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"},
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"}, "flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
"floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"}, "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"}, "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"}, "gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
@ -58,7 +58,7 @@
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"}, "joken": {:hex, :joken, "2.5.0", "09be497d804b8115eb6f07615cef2e60c2a1008fb89dc0aef0d4c4b4609b99aa", [:mix], [{:jose, "~> 1.11.2", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "22b25c89617c5ed8ca7b31026340a25ea0f9ca7160f9706b79be9ed81fdf74e7"},
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"linkify": {:git, "https://akkoma.dev/AkkomaGang/linkify.git", "2567e2c1073fa371fd26fd66dfa5bc77b6919c16", [branch: "bugfix/line-ending-buffer"]}, "linkify": {:git, "https://akkoma.dev/AkkomaGang/linkify.git", "2567e2c1073fa371fd26fd66dfa5bc77b6919c16", [branch: "bugfix/line-ending-buffer"]},
@ -72,7 +72,7 @@
"mfm_parser": {:git, "https://akkoma.dev/AkkomaGang/mfm-parser.git", "912fba81152d4d572e457fd5427f9875b2bc3dbe", [ref: "912fba81152d4d572e457fd5427f9875b2bc3dbe"]}, "mfm_parser": {:git, "https://akkoma.dev/AkkomaGang/mfm-parser.git", "912fba81152d4d572e457fd5427f9875b2bc3dbe", [ref: "912fba81152d4d572e457fd5427f9875b2bc3dbe"]},
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, "mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
"mogrify": {:hex, :mogrify, "0.9.2", "b360984adea7dd6a55f18028e6327973c58de7f548fdb86c9859848aa904d5b0", [:mix], [], "hexpm", "c18d10fd70ca20e2585301616c89f6e4f7159d92efc9cc8ee579e00c886f699d"}, "mogrify": {:hex, :mogrify, "0.9.2", "b360984adea7dd6a55f18028e6327973c58de7f548fdb86c9859848aa904d5b0", [:mix], [], "hexpm", "c18d10fd70ca20e2585301616c89f6e4f7159d92efc9cc8ee579e00c886f699d"},
"mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"}, "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"},
@ -80,20 +80,20 @@
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"oban": {:hex, :oban, "2.12.1", "f604d7e6a8be9fda4a9b0f6cebbd633deba569f85dbff70c4d25d99a6f023177", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b1844c2b74e0d788b73e5144b0c9d5674cb775eae29d88a36f3c3b48d42d058"}, "oban": {:hex, :oban, "2.12.1", "f604d7e6a8be9fda4a9b0f6cebbd633deba569f85dbff70c4d25d99a6f023177", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b1844c2b74e0d788b73e5144b0c9d5674cb775eae29d88a36f3c3b48d42d058"},
"open_api_spex": {:hex, :open_api_spex, "3.16.1", "8137c338129d63060b4b04947c6c57429f86267045c479c703a38a6d3f98dee1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "ef6fd778ac121af866b48b75ad4ad256b6ff33949113ea4aa1629af8bfdfdbfb"}, "open_api_spex": {:hex, :open_api_spex, "3.16.0", "9843af4e87550cd8ac5821b10e4c74f1d51f0d4e3310f824d780614743423b25", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "bb0be24a648b73e8fc8cbda17f514b8486262275e8b33e8b5ae66283df972129"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"}, "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.6", "460c36977643d76fc8e0b6b3c4bba703c0ef21abc74233cf7dc15d1c1696832f", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce2768fb44c3c370df13fc4f0dc70623b662a93a201d8d7d87c4ba6542bc6b73"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.4", "615f8f393135de7e0cbb4bd00ba238b1e0cd324b0d90efbaee613c2f02ca5e5c", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "3971221846232021ab5e3c7489fd62ec5bfd6a2e01cae10a317ccf6fb350571c"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.4", "615f8f393135de7e0cbb4bd00ba238b1e0cd324b0d90efbaee613c2f02ca5e5c", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "3971221846232021ab5e3c7489fd62ec5bfd6a2e01cae10a317ccf6fb350571c"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"}, "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"}, "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
"poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
@ -117,10 +117,10 @@
"telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"}, "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
"temple": {:git, "https://akkoma.dev/AkkomaGang/temple.git", "066a699ade472d8fa42a9d730b29a61af9bc8b59", [ref: "066a699ade472d8fa42a9d730b29a61af9bc8b59"]}, "temple": {:git, "https://akkoma.dev/AkkomaGang/temple.git", "066a699ade472d8fa42a9d730b29a61af9bc8b59", [ref: "066a699ade472d8fa42a9d730b29a61af9bc8b59"]},
"tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"}, "tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"},
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "timex": {:hex, :timex, "3.7.9", "790cdfc4acfce434e442f98c02ea6d84d0239073bfd668968f82ac63e9a6788d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "64691582e5bb87130f721fc709acfb70f24405833998fabf35be968984860ce1"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
"ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "ueberauth": {:hex, :ueberauth, "0.10.3", "4a3bd7ab7b5d93d301d264f0f6858392654ee92171f4437d067d1ae227c051d9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "1394f36a6c64e97f2038cf95228e7e52b4cb75417962e30418fbe9902b30e6d3"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
"vex": {:hex, :vex, "0.9.0", "613ea5eb3055662e7178b83e25b2df0975f68c3d8bb67c1645f0573e1a78d606", [:mix], [], "hexpm", "c69fff44d5c8aa3f1faee71bba1dcab05dd36364c5a629df8bb11751240c857f"}, "vex": {:hex, :vex, "0.9.0", "613ea5eb3055662e7178b83e25b2df0975f68c3d8bb67c1645f0573e1a78d606", [:mix], [], "hexpm", "c69fff44d5c8aa3f1faee71bba1dcab05dd36364c5a629df8bb11751240c857f"},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -54,7 +54,7 @@ test "it returns file" do
assert result == assert result ==
%{ %{
"id" => result["id"], "id" => result["id"],
"name" => "", "name" => "image.jpg",
"type" => "Document", "type" => "Document",
"mediaType" => "image/jpeg", "mediaType" => "image/jpeg",
"url" => [ "url" => [
@ -154,6 +154,19 @@ test "copies the file to the configured folder with deduping" do
"e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
end end
test "copies the file to the configured folder without deduping" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an [image.jpg"
}
{:ok, data} = Upload.store(file)
assert data["name"] == "an [image.jpg"
end
test "fixes incorrect content type when base64 is given" do test "fixes incorrect content type when base64 is given" do
params = %{ params = %{
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}" img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
@ -171,7 +184,7 @@ test "adds extension when base64 is given" do
} }
{:ok, data} = Upload.store(params) {:ok, data} = Upload.store(params)
assert String.ends_with?(List.first(data["url"])["href"], ".jpg") assert String.ends_with?(data["name"], ".jpg")
end end
test "copies the file to the configured folder with anonymizing filename" do test "copies the file to the configured folder with anonymizing filename" do

View file

@ -2509,16 +2509,6 @@ test "avatar fallback" do
assert User.avatar_url(user, no_default: true) == nil assert User.avatar_url(user, no_default: true) == nil
end end
test "avatar object with nil in href" do
user = insert(:user, avatar: %{"url" => [%{"href" => nil}]})
assert User.avatar_url(user) != nil
end
test "banner object with nil in href" do
user = insert(:user, banner: %{"url" => [%{"href" => nil}]})
assert User.banner_url(user) != nil
end
test "get_host/1" do test "get_host/1" do
user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain") user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
assert User.get_host(user) == "lain.com" assert User.get_host(user) == "lain.com"

View file

@ -662,6 +662,35 @@ test "accept follow activity", %{conn: conn} do
assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
end end
@tag capture_log: true
test "without valid signature, " <>
"it only accepts Create activities and requires enabled federation",
%{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
conn = put_req_header(conn, "content-type", "application/activity+json")
clear_config([:instance, :federating], false)
conn
|> post("/inbox", data)
|> json_response(403)
conn
|> post("/inbox", non_create_data)
|> json_response(403)
clear_config([:instance, :federating], true)
ret_conn = post(conn, "/inbox", data)
assert "ok" == json_response(ret_conn, 200)
conn
|> post("/inbox", non_create_data)
|> json_response(400)
end
test "accepts Add/Remove activities", %{conn: conn} do test "accepts Add/Remove activities", %{conn: conn} do
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"

View file

@ -1303,19 +1303,33 @@ test "returns reblogs for users for whom reblogs have not been muted" do
%{test_file: test_file} %{test_file: test_file}
end end
test "strips / from filename", %{test_file: file} do
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
{:ok, %Object{} = object} = ActivityPub.upload(file)
[%{"href" => href}] = object.data["url"]
assert Regex.match?(~r"/bad.jpg$", href)
refute Regex.match?(~r"/nested/", href)
end
test "sets a description if given", %{test_file: file} do test "sets a description if given", %{test_file: file} do
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file") {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
assert object.data["name"] == "a cool file" assert object.data["name"] == "a cool file"
end end
test "it sets the default description depending on the configuration", %{test_file: file} do
clear_config([Pleroma.Upload, :default_description])
clear_config([Pleroma.Upload, :default_description], nil)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == ""
clear_config([Pleroma.Upload, :default_description], :filename)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "an_image.jpg"
clear_config([Pleroma.Upload, :default_description], "unnamed attachment")
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "unnamed attachment"
end
test "copies the file to the configured folder", %{test_file: file} do
clear_config([Pleroma.Upload, :default_description], :filename)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "an_image.jpg"
end
test "works with base64 encoded images" do test "works with base64 encoded images" do
file = %{ file = %{
img: data_uri() img: data_uri()

View file

@ -63,7 +63,7 @@ test "it works for incoming misskey likes that contain unicode emojis, turning t
File.read!("test/fixtures/misskey-like.json") File.read!("test/fixtures/misskey-like.json")
|> Jason.decode!() |> Jason.decode!()
|> Map.put("object", activity.data["object"]) |> Map.put("object", activity.data["object"])
|> Map.put("content", "") |> Map.put("_misskey_reaction", "")
_actor = insert(:user, ap_id: data["actor"], local: false) _actor = insert(:user, ap_id: data["actor"], local: false)

View file

@ -85,40 +85,6 @@ test "a filter with expires_in", %{conn: conn, user: user} do
assert Repo.aggregate(Filter, :count, :id) == 0 assert Repo.aggregate(Filter, :count, :id) == 0
end end
test "a filter with expires_at", %{conn: conn, user: user} do
response =
with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/filters", %{
"phrase" => "bad memes",
context: ["home"],
expires_at: "2017-03-17T17:19:58.000Z"
})
|> json_response_and_validate_schema(200)
end
assert response["irreversible"] == false
assert response["expires_at"] == "2017-03-17T17:19:58.000Z"
filter = Filter.get(response["id"], user)
id = filter.id
assert_enqueued(
worker: PurgeExpiredFilter,
args: %{filter_id: filter.id}
)
assert {:ok, %{id: ^id}} =
perform_job(PurgeExpiredFilter, %{
filter_id: filter.id
})
assert Repo.aggregate(Filter, :count, :id) == 0
end
end end
test "fetching a list of filters" do test "fetching a list of filters" do

View file

@ -124,23 +124,6 @@ test "/api/v2/media, upload_limit", %{conn: conn, user: user} do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data")) assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end end
test "Do not allow nested filename", %{conn: conn, image: image} do
image = %Plug.Upload{
image
| filename: "../../../../../nested/file.jpg"
}
desc = "Description of the image"
media =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/media", %{"file" => image, "description" => desc})
|> json_response_and_validate_schema(:ok)
refute Regex.match?(~r"/nested/", media["url"])
end
end end
describe "Update media description" do describe "Update media description" do

View file

@ -408,25 +408,6 @@ test "should not return local-only posts for anonymous users" do
assert [] = result assert [] = result
end end
test "should return 404 if disabled" do
clear_config([:instance, :federated_timeline_available], false)
result =
build_conn()
|> get("/api/v1/timelines/public")
|> json_response_and_validate_schema(404)
assert %{"error" => "Federated timeline is disabled"} = result
end
test "should not return 404 if local is specified" do
clear_config([:instance, :federated_timeline_available], false)
build_conn()
|> get("/api/v1/timelines/public?local=true")
|> json_response_and_validate_schema(200)
end
end end
defp local_and_remote_activities do defp local_and_remote_activities do
@ -1055,8 +1036,9 @@ test "with `%{local: true, federated: false}`, forbids unauthenticated access to
end end
describe "bubble" do describe "bubble" do
test "filtering" do setup do: oauth_access(["read:statuses"])
%{conn: conn, user: user} = oauth_access(["read:statuses"])
test "filtering", %{conn: conn, user: user} do
clear_config([:instance, :local_bubble], []) clear_config([:instance, :local_bubble], [])
# our endpoint host has a port in it so let's set the AP ID # our endpoint host has a port in it so let's set the AP ID
local_user = insert(:user, %{ap_id: "https://localhost/users/user"}) local_user = insert(:user, %{ap_id: "https://localhost/users/user"})
@ -1090,20 +1072,6 @@ test "filtering" do
assert local_activity.id in two_instances assert local_activity.id in two_instances
assert remote_activity.id in two_instances assert remote_activity.id in two_instances
end end
test "restrict_unauthenticated with bubble timeline", %{conn: conn} do
clear_config([:restrict_unauthenticated, :timelines, :bubble], true)
conn
|> get("/api/v1/timelines/bubble")
|> json_response_and_validate_schema(:unauthorized)
clear_config([:restrict_unauthenticated, :timelines, :bubble], false)
conn
|> get("/api/v1/timelines/bubble")
|> json_response_and_validate_schema(200)
end
end end
defp create_remote_activity(user) do defp create_remote_activity(user) do

View file

@ -396,34 +396,6 @@ test "updates the user's background, upload_limit, returns a HTTP 413", %{
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data")) assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end end
test "Strip / from upload files", %{user: user, conn: conn} do
new_image = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "../../../../nested/an_image.jpg"
}
assert user.avatar == %{}
res =
patch(conn, "/api/v1/accounts/update_credentials", %{
"avatar" => new_image,
"header" => new_image,
"pleroma_background_image" => new_image
})
assert user_response = json_response_and_validate_schema(res, 200)
assert user_response["avatar"]
assert user_response["header"]
assert user_response["pleroma"]["background_image"]
refute Regex.match?(~r"/nested/", user_response["avatar"])
refute Regex.match?(~r"/nested/", user_response["header"])
refute Regex.match?(~r"/nested/", user_response["pleroma"]["background_image"])
user = User.get_by_id(user.id)
refute user.avatar == %{}
end
test "requires 'write:accounts' permission" do test "requires 'write:accounts' permission" do
token1 = insert(:oauth_token, scopes: ["read"]) token1 = insert(:oauth_token, scopes: ["read"])
token2 = insert(:oauth_token, scopes: ["write", "follow"]) token2 = insert(:oauth_token, scopes: ["write", "follow"])

View file

@ -269,8 +269,8 @@ test "Represent a Service(bot) account" do
} }
with_mock( with_mock(
Pleroma.Web.Nodeinfo.Nodeinfo, Pleroma.Web.Nodeinfo.NodeinfoController,
get_nodeinfo: fn _ -> %{version: "2.0"} end raw_nodeinfo: fn -> %{version: "2.0"} end
) do ) do
assert expected == assert expected ==
AccountView.render("show.json", %{user: user, skip_visibility_check: true}) AccountView.render("show.json", %{user: user, skip_visibility_check: true})

View file

@ -292,38 +292,4 @@ test "shows extra information in the mrf_simple_info field for relevant entries"
assert response["metadata"]["federation"]["mrf_simple_info"] == expected_config assert response["metadata"]["federation"]["mrf_simple_info"] == expected_config
end end
end end
describe "public timeline visibility" do
test "shows public timeline visibility", %{conn: conn} do
clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: false})
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)
assert response["metadata"]["publicTimelineVisibility"]["local"] == true
assert response["metadata"]["publicTimelineVisibility"]["federated"] == true
clear_config([:restrict_unauthenticated, :timelines], %{local: true, federated: false})
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)
assert response["metadata"]["publicTimelineVisibility"]["local"] == false
assert response["metadata"]["publicTimelineVisibility"]["federated"] == true
clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: true})
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)
assert response["metadata"]["publicTimelineVisibility"]["local"] == true
assert response["metadata"]["publicTimelineVisibility"]["federated"] == false
end
end
end end

View file

@ -728,42 +728,6 @@ test "redirects with oauth authorization, " <>
assert auth.scopes == scopes_subset assert auth.scopes == scopes_subset
end end
test "redirects with oauth authorization, " <>
"granting requested app-supported scopes to moderators" do
app_scopes = ["read", "write", "admin", "secret_scope"]
app = insert(:oauth_app, scopes: app_scopes)
redirect_uri = OAuthController.default_redirect_uri(app)
scopes_subset = ["read:subscope", "write", "admin"]
admin = insert(:user, is_moderator: true)
# In case scope param is missing, expecting _all_ app-supported scopes to be granted
conn =
post(
build_conn(),
"/oauth/authorize",
%{
"authorization" => %{
"name" => admin.nickname,
"password" => "test",
"client_id" => app.client_id,
"redirect_uri" => redirect_uri,
"scope" => scopes_subset,
"state" => "statepassed"
}
}
)
target = redirected_to(conn)
assert target =~ redirect_uri
query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
assert %{"state" => "statepassed", "code" => code} = query
auth = Repo.get_by(Authorization, token: code)
assert auth
assert auth.scopes == scopes_subset
end
test "redirects with oauth authorization, " <> test "redirects with oauth authorization, " <>
"granting requested app-supported scopes for non-admin users" do "granting requested app-supported scopes for non-admin users" do
app_scopes = ["read", "write", "secret_scope", "admin"] app_scopes = ["read", "write", "secret_scope", "admin"]

View file

@ -83,7 +83,6 @@ test "api routes are detected correctly" do
"main", "main",
"ostatus_subscribe", "ostatus_subscribe",
"oauth", "oauth",
"akkoma",
"objects", "objects",
"activities", "activities",
"notice", "notice",

View file

@ -69,47 +69,6 @@ test "it considers a mapped identity to be invalid when the associated instance
assert %{valid_signature: false} == conn.assigns assert %{valid_signature: false} == conn.assigns
end end
test "allowlist federation: it considers a mapped identity to be valid when the associated instance is allowed" do
clear_config([:activitypub, :authorized_fetch_mode], true)
clear_config([:mrf_simple, :accept], [
{"mastodon.example.org", "anime is allowed"}
])
on_exit(fn ->
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false)
Pleroma.Config.put([:mrf_simple, :accept], [])
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|> set_signature("http://mastodon.example.org/users/admin")
|> MappedSignatureToIdentityPlug.call(%{})
assert conn.assigns[:valid_signature]
refute is_nil(conn.assigns.user)
end
test "allowlist federation: it considers a mapped identity to be invalid when the associated instance is not allowed" do
clear_config([:activitypub, :authorized_fetch_mode], true)
clear_config([:mrf_simple, :accept], [
{"misskey.example.org", "anime is allowed"}
])
on_exit(fn ->
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false)
Pleroma.Config.put([:mrf_simple, :accept], [])
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|> set_signature("http://mastodon.example.org/users/admin")
|> MappedSignatureToIdentityPlug.call(%{})
assert %{valid_signature: false} == conn.assigns
end
@tag skip: "known breakage; the testsuite presently depends on it" @tag skip: "known breakage; the testsuite presently depends on it"
test "it considers a mapped identity to be invalid when the identity cannot be found" do test "it considers a mapped identity to be invalid when the identity cannot be found" do
conn = conn =

View file

@ -26,12 +26,13 @@ test "parse/1" do
test "maybe_put_rel_me/2" do test "maybe_put_rel_me/2" do
profile_urls = ["https://social.example.org/users/lain"] profile_urls = ["https://social.example.org/users/lain"]
attr = "me" attr = "me"
fallback = nil
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) == assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) ==
{:error, {:could_not_verify, "http://example.com/rel_me/null", {:link_match, false}}} fallback
assert {:error, {:could_not_fetch, "http://example.com/rel_me/error", _}} = assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) ==
Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) fallback
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) == assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) ==
attr attr