Compare commits

...

62 Commits

Author SHA1 Message Date
Puniko 00f79c3a11 resolve merge conflicts 2023-04-22 07:53:46 +02:00
FloatingGhost d6bed599c8 correct version bump 2023-04-14 18:09:59 +01:00
FloatingGhost 963d29ad8c 2023.04 Release 2023-04-14 18:00:59 +01:00
FloatingGhost f2b4e7f86b Merge branch 'develop' of akkoma.dev:AkkomaGang/akkoma into develop 2023-04-14 17:56:56 +01:00
FloatingGhost 522221f7fb Mix format 2023-04-14 17:56:34 +01:00
Atsuko Karagi 1fa3c0b485 Remove support for outdated Create format 2023-04-14 17:46:22 +01:00
Atsuko Karagi d2b0d86471 HTTP signatures respect allowlist federation 2023-04-14 17:46:06 +01:00
FloatingGhost f12d3cce39 ensure only pickable frontends can be returned 2023-04-14 17:42:40 +01:00
floatingghost 8c86a06ed1 Merge pull request 'Remove "default" image description' (#493) from ilja/akkoma:remove_default_image_description into develop
Reviewed-on: AkkomaGang/akkoma#493
2023-04-14 16:27:41 +00:00
FloatingGhost ba59fdcd54 add changelog entry 2023-04-14 16:56:51 +01:00
FloatingGhost 4c9c959bb3 Merge branch 'develop' into frontend-switcher-9000 2023-04-14 16:56:10 +01:00
FloatingGhost 9e8e7cc13e Add note telling people to refresh 2023-04-14 16:55:48 +01:00
FloatingGhost a079ec3a3c in dev, allow dev FE 2023-04-14 16:36:40 +01:00
FloatingGhost 1b2c24a19e fix tests 2023-04-14 15:20:55 +01:00
floatingghost 62e22eeff2 Merge pull request 'Update elixir versions' (#512) from norm/akkoma:update-elixir-versions into develop
Reviewed-on: AkkomaGang/akkoma#512
2023-04-11 09:28:01 +00:00
floatingghost ca1accc1cf Merge pull request 'Add YunoHost to installation guides' (#518) from ilja/akkoma:docs_ynh_installation into develop
Reviewed-on: AkkomaGang/akkoma#518
2023-04-11 09:26:38 +00:00
ilja d8d9edee98 Add YunoHost to installation guides 2023-04-03 11:22:53 +02:00
FloatingGhost 2a8c1f4192 Add extra diagnostic tasks in 2023-03-29 14:11:00 +01:00
FloatingGhost 66d162bb9e Add debug logs to timeline rendering to assist debugging 2023-03-29 12:01:16 +01:00
FloatingGhost d85d1e128a we don't actually need the object on redirect 2023-03-29 11:44:03 +01:00
floatingghost ef8f13a158 Merge pull request 'I LOVE OBJECTS!!' (#517) from wowee into develop
Reviewed-on: AkkomaGang/akkoma#517
2023-03-29 02:33:51 +00:00
sadposter 0151ca1d52 Revert "Remove indexer plugin"
This reverts commit 1d94f2a424.
2023-03-29 03:32:30 +01:00
sadposter 3f340cbc43 Only even attempt to fetch local activities by object_id
TODO: PLEASE FOR THE LOVE OF KANATAN CACHE THIS
2023-03-29 03:32:24 +01:00
FloatingGhost 1d94f2a424 Remove indexer plugin 2023-03-29 01:59:19 +01:00
FloatingGhost de64c6c54a add selection UI 2023-03-28 12:44:52 +01:00
FloatingGhost 4bbe9c8f5c Ship with hehe 2023-03-27 10:03:12 +01:00
floatingghost 281c4636fa Merge pull request 'Show bubble_timeline in the api if any instances are set in it' (#502) from foxing/akkoma:foxing-patch-1 into develop
Reviewed-on: AkkomaGang/akkoma#502
2023-03-21 10:13:41 +00:00
FloatingGhost f94e8a3713 add bubble visibility to description 2023-03-18 20:49:43 +00:00
FloatingGhost dd44387f1a Add timeline visibility options 2023-03-17 15:33:28 +00:00
Norm 63870c2c17 Update base image in Dockerfile to newer elixir version 2023-03-16 12:55:04 -04:00
Norm 3c30666d3f Update elixir and erlang versions in docs 2023-03-16 12:54:38 -04:00
Norm f22bba6359 Update elixir version in elixir_buildpack.config 2023-03-16 12:54:15 -04:00
Norm 4a5164be93 Update required elixir version in mix.exs to 1.14 2023-03-16 12:53:38 -04:00
FloatingGhost fe7045632b also put publicVisibility in preloaded nodeinfo 2023-03-15 22:59:58 +00:00
FloatingGhost 86a5cf3c82 Changelog entry 2023-03-15 22:20:32 +00:00
FloatingGhost 2c9e02429a mix format 2023-03-15 22:19:52 +00:00
FloatingGhost 9464d50562 Add publicTimelineVisibility to nodeinfo 2023-03-15 22:13:18 +00:00
foxing bd040fe96a Merge branch 'develop' into foxing-patch-1 2023-03-13 03:41:15 +00:00
foxing ba635e97c8 Use enum empty instead 2023-03-13 03:40:20 +00:00
floatingghost 377d1483b6 Merge pull request 'Apply security patch from pleroma to prevent nested file names being uploaded to the server.' (#507) from foxing/akkoma:foxing-patch-2 into develop
Reviewed-on: AkkomaGang/akkoma#507
2023-03-13 00:29:51 +00:00
floatingghost c5769bbf6d Merge pull request 'don't crash on malformed avatar and banner values' (#506) from flisk/akkoma:fix-crash-malformed-avatars-banners into develop
Reviewed-on: AkkomaGang/akkoma#506
2023-03-13 00:28:16 +00:00
FloatingGhost 643b8c5f15 ensure we send the right files for preferred fe 2023-03-12 23:59:10 +00:00
FloatingGhost 3d964a9970 Add frontend preference route 2023-03-12 23:24:07 +00:00
foxing c2ae3273d5 Merge branch 'develop' into foxing-patch-2 2023-03-12 19:23:22 +00:00
foxing 3f76de76da Apply Patch 2023-03-12 19:13:56 +00:00
flisk 0c77be9308 don't crash on malformed avatar and banner values
weird values in href will cause base64 encoding to fail later down the
line, so let's make sure the value we're passing on is somewhat sane, or
at the very least a binary

this fixes #482
2023-03-12 18:14:05 +01:00
ilja 6c396fcab4 Remove "default" image description
When no image description is filled in, Pleroma allowed fallbacks.
Those were (based on a setting) either the filename, or a fixed description.
Neither are good options for image descriptions imo, so here we remove this.

Note that there's two tests removed who supposedly tested something else.
But examining closer, they didn't seem to test what they claimed to test,
so I removed them rather than try to "fix" them.
2023-03-12 08:42:33 +01:00
foxing e17d8f744e Merge branch 'develop' into foxing-patch-1 2023-03-11 19:09:14 +00:00
FloatingGhost 58f75ac062 patch version 2023-03-11 18:24:57 +00:00
FloatingGhost 70803d7966 Remove mix.env reference 2023-03-11 18:24:44 +00:00
FloatingGhost 800fe40407 Bump version 2023-03-11 17:26:21 +00:00
FloatingGhost 5ca22c2459 ensure we can't have a null in appends 2023-03-11 17:24:49 +00:00
foxing 19eb826424 Show bubble_timeline in the api if any instances are set in it, do not show if none are set 2023-03-11 03:26:48 +00:00
FloatingGhost 9977588612 we should probably use || 2023-03-10 18:49:08 +00:00
floatingghost e124a109c1 Remove _misskey_reaction matching (#500)
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/akkoma#500
2023-03-10 18:46:49 +00:00
floatingghost 592340a49d Merge pull request 'Fix typo in installation/akkoma.service' (#498) from quad/akkoma:develop into develop
Reviewed-on: AkkomaGang/akkoma#498
2023-03-10 17:20:33 +00:00
quad f1e836b183 Fix typo in installation/akkoma.service 2023-03-10 15:51:56 +00:00
FloatingGhost 08dfce98be Merge branch 'develop' of akkoma.dev:AkkomaGang/akkoma into develop 2023-03-10 03:51:45 +00:00
FloatingGhost b2112302ce Add more information about failed verifications 2023-03-10 03:51:24 +00:00
foxing 964a855319 Display Quote posts in the api features list to allow external clients to enable compatibility with it. (#496)
Expose quote posting in the api as a feature.

Copies what the quote post PR for pleroma does to allow external clients to enable and disable features based on the feature-set of the instance.

As far as I am aware, akkoma doesn't allow you to disable quote posting, so this doesn't need anything fancy and it's just a hard on switch.

I tried to get one for the bubble tl to work also, but I'm not quite sure how to do it so that it switches off the feature when the bubble tl is disabled. I would argue that it could and ideally should be done as well though.

I also discovered a pretty tame bug in the testing of it, that deleting the DB entry for the bubble tl does not stop the bubble TL from actually working and it will continue to display the panel on the about page, I'll just leave it as a note here.

Reviewed-on: AkkomaGang/akkoma#496
Co-authored-by: foxing <foxing@noreply.akkoma>
Co-committed-by: foxing <foxing@noreply.akkoma>
2023-03-09 20:40:28 +00:00
FloatingGhost 8a4437d2be Allow expires_at in filter requests
Fixes #492
2023-03-09 19:13:14 +00:00
FloatingGhost 87d5e5b06a Allow moderators to get the admin scope again
Fixes #463
2023-03-08 17:39:35 +00:00
63 changed files with 753 additions and 328 deletions

View File

@ -4,19 +4,36 @@ 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/).
## Unreleased
## 2023.04
## 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
- Allowed contentMap to be updated on edit
- Filter creation now accepts expires\_at
### Changed
- Restoring the database from a dump now goes much faster without need for work-arounds
- Misskey reaction matching uses `content` parameter now
### Added
- Extend the mix task `prune_objects` with option `--prune-orphaned-activities` to also prune orphaned activities, allowing to reclaim even more database space
### Removed
- Possibility of using the `style` parameter on `span` elements. This will break certain MFM parameters.
- Option for "default" image description.
## 2023.02

View File

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

View File

@ -54,6 +54,9 @@ If your platform is not supported, or you just want to be able to edit the sourc
### Docker
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
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,7 +65,6 @@ config :pleroma, Pleroma.Upload,
link_name: false,
proxy_remote: false,
filename_display_max_length: 30,
default_description: nil,
base_url: nil
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
@ -261,7 +260,8 @@ config :pleroma, :instance,
privileged_staff: false,
local_bubble: [],
max_frontend_settings_json_chars: 100_000,
export_prometheus_metrics: true
export_prometheus_metrics: true,
federated_timeline_available: true
config :pleroma, :welcome,
direct_message: [
@ -745,6 +745,9 @@ config :pleroma, :frontends,
primary: %{"name" => "pleroma-fe", "ref" => "stable"},
admin: %{"name" => "admin-fe", "ref" => "stable"},
mastodon: %{"name" => "mastodon-fe", "ref" => "akkoma"},
pickable: [
"pleroma-fe/stable"
],
swagger: %{
"name" => "swagger-ui",
"ref" => "stable",
@ -810,7 +813,7 @@ config :pleroma, :majic_pool, size: 2
private_instance? = :if_instance_is_private
config :pleroma, :restrict_unauthenticated,
timelines: %{local: private_instance?, federated: private_instance?},
timelines: %{local: private_instance?, federated: private_instance?, bubble: true},
profiles: %{local: private_instance?, remote: private_instance?},
activities: %{local: private_instance?, remote: private_instance?}

View File

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

View File

@ -969,6 +969,12 @@ config :pleroma, :config_description, [
key: :export_prometheus_metrics,
type: :boolean,
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."
}
]
},
@ -2993,6 +2999,11 @@ config :pleroma, :config_description, [
key: :federated,
type: :boolean,
description: "Disallow viewing the whole known network timeline."
},
%{
key: :bubble,
type: :boolean,
description: "Disallow viewing the bubble timeline."
}
]
},
@ -3148,6 +3159,12 @@ config :pleroma, :config_description, [
description:
"A map containing available frontends and parameters for their installation.",
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,8 +23,7 @@ config :pleroma, :auth, oauth_consumer_strategies: []
config :pleroma, Pleroma.Upload,
filters: [],
link_name: false,
default_description: :filename
link_name: false
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"

View File

@ -562,7 +562,6 @@ 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_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
* `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
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.

View File

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

View File

@ -0,0 +1,9 @@
# 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.9.4
erlang_version=22.3.4.1
elixir_version=1.14.3
erlang_version=25.3

View File

@ -7,7 +7,7 @@ ExecReload=/bin/kill $MAINPID
Restart=on-failure
; Uncomment this if you're on Arch Linux
; Evironment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl"
; Environment="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.
User=akkoma

View File

@ -82,4 +82,46 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity)
|> IO.puts()
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

View File

@ -277,6 +277,13 @@ defmodule Pleroma.Activity do
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
def create_by_id_with_object(id) do
get_by_id_with_opts(id, preload: [:object], filter: [type: "Create"])

View File

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

View File

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

View File

@ -65,15 +65,6 @@ defmodule Pleroma.Upload do
}
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()}
@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
@ -82,7 +73,7 @@ defmodule Pleroma.Upload do
with {:ok, upload} <- prepare_upload(upload, opts),
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
description = get_description(opts, upload),
description = Map.get(opts, :description) || "",
{_, true} <-
{:description_limit,
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},

View File

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

View File

@ -1502,13 +1502,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(file, opts) do
with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
Repo.insert(%Object{data: obj_data})
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
defp get_actor_url(url) when is_binary(url), do: url
defp get_actor_url(%{"href" => href}) when is_binary(href), do: href

View File

@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Activity
alias Pleroma.Delivery
alias Pleroma.Object
alias Pleroma.Object.Fetcher
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.InternalFetchActor
@ -293,33 +292,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> json("Invalid HTTP Signature")
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
conn
|> put_status(:bad_request)
|> json("error, missing HTTP Signature")
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
conn
|> put_resp_content_type("application/activity+json")

View File

@ -13,7 +13,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@primary_key false
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
embedded_schema do
quote do
@ -75,9 +74,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
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
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
@ -98,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
defp validate_emoji(cng) do
content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) || matches_shortcode?(content) do
if Emoji.is_unicode_emoji?(content) || Emoji.matches_shortcode?(content) do
cng
else
cng

View File

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

View File

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

View File

@ -5,6 +5,16 @@ defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do
alias Pleroma.Akkoma.FrontendSettingsProfile
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
plug(
OAuthScopesPlug,
@unauthenticated_access
when action in [
:available_frontends,
:update_preferred_frontend
]
)
plug(
OAuthScopesPlug,
%{@unauthenticated_access | scopes: ["read:accounts"]}
@ -93,4 +103,22 @@ defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do
|> json(profile.settings)
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

View File

@ -0,0 +1,20 @@
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

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

View File

@ -225,6 +225,12 @@ defmodule Pleroma.Web.ApiSpec.FilterOperation do
type: :integer,
description:
"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],

View File

@ -12,7 +12,7 @@ defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
@spec list_profiles_operation() :: Operation.t()
def list_profiles_operation() do
%Operation{
tags: ["Retrieve frontend setting profiles"],
tags: ["Frontends"],
summary: "Frontend Settings Profiles",
description: "List frontend setting profiles",
operationId: "AkkomaAPI.FrontendSettingsController.list_profiles",
@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
@spec get_profile_operation() :: Operation.t()
def get_profile_operation() do
%Operation{
tags: ["Retrieve frontend setting profile"],
tags: ["Frontends"],
summary: "Frontend Settings Profile",
description: "Get frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.get_profile",
@ -60,7 +60,7 @@ defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
@spec delete_profile_operation() :: Operation.t()
def delete_profile_operation() do
%Operation{
tags: ["Delete frontend setting profile"],
tags: ["Frontends"],
summary: "Delete frontend Settings Profile",
description: "Delete frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.delete_profile",
@ -76,7 +76,7 @@ defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
@spec update_profile_operation() :: Operation.t()
def update_profile_operation() do
%Operation{
tags: ["Update frontend setting profile"],
tags: ["Frontends"],
summary: "Frontend Settings Profile",
description: "Update frontend setting profile",
operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation",
@ -90,6 +90,57 @@ defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
}
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
Operation.parameter(:frontend_name, :path, :string, "Frontend name",
example: "pleroma-fe",

View File

@ -70,7 +70,8 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
operationId: "TimelineController.public",
responses: %{
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

View File

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

View File

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

View File

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

View File

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

View File

@ -71,7 +71,15 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
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

View File

@ -5,12 +5,8 @@
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
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.Nodeinfo.Nodeinfo
def schemas(conn, _params) do
response = %{
@ -29,101 +25,15 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
json(conn, response)
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
# and https://github.com/jhass/nodeinfo/blob/master/schemas/2.1/schema.json
def nodeinfo(conn, %{"version" => "2.0"}) do
def nodeinfo(conn, %{"version" => version}) when version in ["2.0", "2.1"] do
conn
|> put_resp_header(
"content-type",
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
)
|> 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)
|> json(Nodeinfo.get_nodeinfo(version))
end
def nodeinfo(conn, _) do

View File

@ -71,6 +71,8 @@ defmodule Pleroma.Web.OAuth.Scopes do
"""
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
drop_scopes = OAuthScopesPlug.filter_descendants(scopes, ["admin"])
Enum.reject(scopes, fn scope -> Enum.member?(drop_scopes, scope) end)

View File

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

View File

@ -5,17 +5,23 @@
defmodule Pleroma.Web.Plugs.FrontendStatic do
require Pleroma.Constants
@frontend_cookie_name "preferred_frontend"
@moduledoc """
This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends.
"""
@behaviour Plug
def file_path(path, frontend_type \\ :primary) do
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
defp instance_static_path 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
Path.join([
instance_static_path,
instance_static_path(),
"frontends",
configuration["name"],
configuration["ref"],
@ -26,6 +32,15 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
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
opts
|> Keyword.put(:from, "__unconfigured_frontend_static_plug")
@ -38,7 +53,8 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
with false <- api_route?(conn.path_info),
false <- invalid_path?(conn.path_info),
true <- enabled?(opts[:if]),
frontend_type <- Map.get(opts, :frontend_type, :primary),
fallback_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
call_static(conn, opts, path)
else
@ -47,6 +63,31 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
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?(true), do: true
defp enabled?(_), do: false

View File

@ -8,6 +8,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
require Logger
@mix_env Mix.env()
def init(opts), do: opts
def call(conn, _options) do
@ -116,6 +118,13 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
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"]
insecure = if scheme == "https", do: "upgrade-insecure-requests"

View File

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

View File

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

View File

@ -466,6 +466,29 @@ defmodule Pleroma.Web.Router do
put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create)
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
pipe_through(:authenticated_api)
get("/metrics", MetricsController, :show)
@ -598,7 +621,6 @@ defmodule Pleroma.Web.Router do
get("/timelines/home", TimelineController, :home)
get("/timelines/direct", TimelineController, :direct)
get("/timelines/list/:list_id", TimelineController, :list)
get("/timelines/bubble", TimelineController, :bubble)
get("/announcements", AnnouncementController, :index)
post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
@ -653,6 +675,7 @@ defmodule Pleroma.Web.Router do
get("/timelines/public", TimelineController, :public)
get("/timelines/tag/:tag", TimelineController, :hashtag)
get("/timelines/bubble", TimelineController, :bubble)
get("/polls/:id", PollController, :show)

View File

@ -0,0 +1,10 @@
<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,6 +19,7 @@
<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/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'>
<script crossorigin='anonymous' src="/packs/js/application.js"></script>

View File

@ -4,8 +4,8 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
version: version("3.6.0"),
elixir: "~> 1.12",
version: version("3.8.0"),
elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix] ++ Mix.compilers(),
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"},
"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"},
"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"},
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.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"]},
"castore": {:hex, :castore, "0.1.20", "62a0126cbb7cb3e259257827b9190f88316eb7aa3fdac01fd6f2dfd64e7f46e9", [:mix], [], "hexpm", "a020b7650529c986c454a4035b6b13a328e288466986307bea3aadb4c95ac98a"},
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
@ -24,10 +24,10 @@
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"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"},
"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.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"earmark": {:hex, :earmark, "1.4.37", "56ce845c543393aa3f9b294c818c3d783452a4a67e4ab18c4303a954a8b59363", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "d86d5e12868db86d5321b00e62a4bbcb4150346e4acc9a90a041fb188a5cb106"},
"earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"},
"eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"},
"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": {: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_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_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_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_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_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_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"},
"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"},
"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"},
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"},
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
"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"},
@ -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"},
"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"},
"joken": {:hex, :joken, "2.5.0", "09be497d804b8115eb6f07615cef2e60c2a1008fb89dc0aef0d4c4b4609b99aa", [:mix], [{:jose, "~> 1.11.2", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "22b25c89617c5ed8ca7b31026340a25ea0f9ca7160f9706b79be9ed81fdf74e7"},
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"},
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"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"]},
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"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"},
"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"},
"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"},
"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_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"},
"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"},
"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"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"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": {: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_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.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.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.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_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_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_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_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.14.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.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.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"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_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"plug_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"},
"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"},
"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"},
"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"},
"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"},
"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"},
"ueberauth": {:hex, :ueberauth, "0.10.3", "4a3bd7ab7b5d93d301d264f0f6858392654ee92171f4437d067d1ae227c051d9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "1394f36a6c64e97f2038cf95228e7e52b4cb75417962e30418fbe9902b30e6d3"},
"ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
"vex": {:hex, :vex, "0.9.0", "613ea5eb3055662e7178b83e25b2df0975f68c3d8bb67c1645f0573e1a78d606", [:mix], [], "hexpm", "c69fff44d5c8aa3f1faee71bba1dcab05dd36364c5a629df8bb11751240c857f"},

BIN
priv/static/emoji/hehe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -54,7 +54,7 @@ defmodule Pleroma.UploadTest do
assert result ==
%{
"id" => result["id"],
"name" => "image.jpg",
"name" => "",
"type" => "Document",
"mediaType" => "image/jpeg",
"url" => [
@ -154,19 +154,6 @@ defmodule Pleroma.UploadTest do
"e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
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
params = %{
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
@ -184,7 +171,7 @@ defmodule Pleroma.UploadTest do
}
{:ok, data} = Upload.store(params)
assert String.ends_with?(data["name"], ".jpg")
assert String.ends_with?(List.first(data["url"])["href"], ".jpg")
end
test "copies the file to the configured folder with anonymizing filename" do

View File

@ -2509,6 +2509,16 @@ defmodule Pleroma.UserTest do
assert User.avatar_url(user, no_default: true) == nil
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
user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
assert User.get_host(user) == "lain.com"

View File

@ -662,35 +662,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
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
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"

View File

@ -1303,33 +1303,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
%{test_file: test_file}
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
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
assert object.data["name"] == "a cool file"
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
file = %{
img: data_uri()

View File

@ -63,7 +63,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do
File.read!("test/fixtures/misskey-like.json")
|> Jason.decode!()
|> Map.put("object", activity.data["object"])
|> Map.put("_misskey_reaction", "")
|> Map.put("content", "")
_actor = insert(:user, ap_id: data["actor"], local: false)

View File

@ -85,6 +85,40 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
assert Repo.aggregate(Filter, :count, :id) == 0
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
test "fetching a list of filters" do

View File

@ -124,6 +124,23 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
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
describe "Update media description" do

View File

@ -408,6 +408,25 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
assert [] = result
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
defp local_and_remote_activities do
@ -1036,9 +1055,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
end
describe "bubble" do
setup do: oauth_access(["read:statuses"])
test "filtering", %{conn: conn, user: user} do
test "filtering" do
%{conn: conn, user: user} = oauth_access(["read:statuses"])
clear_config([:instance, :local_bubble], [])
# 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"})
@ -1060,7 +1078,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
assert local_activity.id in one_instance
# If we have others, also include theirs
# If we have others, also include theirs
clear_config([:instance, :local_bubble], ["example.com"])
two_instances =
@ -1072,6 +1090,20 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
assert local_activity.id in two_instances
assert remote_activity.id in two_instances
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
defp create_remote_activity(user) do

View File

@ -396,6 +396,34 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
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
token1 = insert(:oauth_token, scopes: ["read"])
token2 = insert(:oauth_token, scopes: ["write", "follow"])

View File

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

View File

@ -292,4 +292,38 @@ defmodule Pleroma.Web.NodeInfoTest do
assert response["metadata"]["federation"]["mrf_simple_info"] == expected_config
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

View File

@ -728,6 +728,42 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
assert auth.scopes == scopes_subset
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, " <>
"granting requested app-supported scopes for non-admin users" do
app_scopes = ["read", "write", "secret_scope", "admin"]

View File

@ -83,6 +83,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
"main",
"ostatus_subscribe",
"oauth",
"akkoma",
"objects",
"activities",
"notice",

View File

@ -69,6 +69,47 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do
assert %{valid_signature: false} == conn.assigns
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"
test "it considers a mapped identity to be invalid when the identity cannot be found" do
conn =

View File

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