Merge branch 'develop' into feature/compat/push-subscriptions
# Conflicts: # lib/pleroma/application.ex # lib/pleroma/plugs/oauth_plug.ex
This commit is contained in:
commit
4944498133
5
.gitignore
vendored
5
.gitignore
vendored
@ -6,6 +6,9 @@
|
||||
/uploads
|
||||
/test/uploads
|
||||
/.elixir_ls
|
||||
/test/fixtures/test_tmp.txt
|
||||
/test/fixtures/image_tmp.jpg
|
||||
/doc
|
||||
|
||||
# Prevent committing custom emojis
|
||||
/priv/static/emoji/custom/*
|
||||
@ -28,4 +31,4 @@ erl_crash.dump
|
||||
.env
|
||||
|
||||
# Editor config
|
||||
/.vscode
|
||||
/.vscode
|
||||
|
@ -10,18 +10,18 @@ config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||
|
||||
config :pleroma, Pleroma.Repo, types: Pleroma.PostgresTypes
|
||||
|
||||
# Upload configuration
|
||||
config :pleroma, Pleroma.Upload,
|
||||
uploader: Pleroma.Uploaders.Local,
|
||||
strip_exif: false
|
||||
filters: [],
|
||||
proxy_remote: false,
|
||||
proxy_opts: []
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.Local,
|
||||
uploads: "uploads",
|
||||
uploads_url: "{{base_url}}/media/{{file}}"
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.S3,
|
||||
bucket: nil,
|
||||
public_endpoint: "https://s3.amazonaws.com",
|
||||
force_media_proxy: false
|
||||
public_endpoint: "https://s3.amazonaws.com"
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.MDII,
|
||||
cgi: "https://mdii.sakura.ne.jp/mdii-post.cgi",
|
||||
@ -72,6 +72,7 @@ config :mime, :types, %{
|
||||
config :pleroma, :websub, Pleroma.Web.Websub
|
||||
config :pleroma, :ostatus, Pleroma.Web.OStatus
|
||||
config :pleroma, :httpoison, Pleroma.HTTP
|
||||
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||
|
||||
# Configures http settings, upstream proxy etc.
|
||||
config :pleroma, :http, proxy_url: nil
|
||||
@ -150,9 +151,11 @@ config :pleroma, :mrf_simple,
|
||||
|
||||
config :pleroma, :media_proxy,
|
||||
enabled: false,
|
||||
redirect_on_failure: true
|
||||
|
||||
# base_url: "https://cache.pleroma.social"
|
||||
# base_url: "https://cache.pleroma.social",
|
||||
proxy_opts: [
|
||||
# inline_content_types: [] | false | true,
|
||||
# http: [:insecure]
|
||||
]
|
||||
|
||||
config :pleroma, :chat, enabled: true
|
||||
|
||||
|
@ -5,11 +5,19 @@ If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherw
|
||||
|
||||
## Pleroma.Upload
|
||||
* `uploader`: Select which `Pleroma.Uploaders` to use
|
||||
* `strip_exif`: boolean, uses ImageMagick(!) to strip exif.
|
||||
* `filters`: List of `Pleroma.Upload.Filter` to use.
|
||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
||||
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
||||
|
||||
Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
||||
|
||||
## Pleroma.Uploaders.Local
|
||||
* `uploads`: Which directory to store the user-uploads in, relative to pleroma’s working directory
|
||||
* `uploads_url`: The URL to access a user-uploaded file, ``{{base_url}}`` is replaced to the instance URL and ``{{file}}`` to the filename. Useful when you want to proxy the media files via another host.
|
||||
|
||||
## Pleroma.Upload.Filter.Mogrify
|
||||
|
||||
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", {"impode", "1"}]`.
|
||||
|
||||
## :uri_schemes
|
||||
* `valid_schemes`: List of the scheme part that is considered valid to be an URL
|
||||
@ -68,7 +76,8 @@ This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:i
|
||||
|
||||
## :media_proxy
|
||||
* `enabled`: Enables proxying of remote media to the instance’s proxy
|
||||
* `redirect_on_failure`: Use the original URL when Media Proxy fails to get it
|
||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
||||
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
||||
|
||||
## :gopher
|
||||
* `enabled`: Enables the gopher interface
|
||||
|
@ -49,11 +49,10 @@ config :pleroma, Pleroma.Repo,
|
||||
hostname: "localhost",
|
||||
pool_size: 10
|
||||
|
||||
try do
|
||||
if File.exists?("./config/dev.secret.exs") do
|
||||
import_config "dev.secret.exs"
|
||||
rescue
|
||||
_ ->
|
||||
IO.puts(
|
||||
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
||||
)
|
||||
else
|
||||
IO.puts(
|
||||
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
||||
)
|
||||
end
|
||||
|
@ -9,7 +9,7 @@ config :pleroma, Pleroma.Web.Endpoint,
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
|
||||
config :pleroma, Pleroma.Upload, uploads: "test/uploads"
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||
|
||||
# Configure your database
|
||||
config :pleroma, Pleroma.Repo,
|
||||
@ -25,7 +25,7 @@ config :pbkdf2_elixir, rounds: 1
|
||||
|
||||
config :pleroma, :websub, Pleroma.Web.WebsubMock
|
||||
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
||||
config :pleroma, :httpoison, HTTPoisonMock
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
try do
|
||||
import_config "test.secret.exs"
|
||||
|
@ -70,10 +70,12 @@ server {
|
||||
client_max_body_size 16m;
|
||||
}
|
||||
|
||||
location /proxy {
|
||||
location ~ ^/(media|proxy) {
|
||||
proxy_cache pleroma_media_cache;
|
||||
proxy_cache_lock on;
|
||||
proxy_ignore_client_abort on;
|
||||
proxy_buffering off;
|
||||
chunked_transfer_encoding on;
|
||||
proxy_pass http://localhost:4000;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ defmodule Mix.Tasks.SetModerator do
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
import Mix.Ecto
|
||||
import Ecto.Changeset
|
||||
alias Pleroma.{Repo, User}
|
||||
|
||||
def run([nickname | rest]) do
|
||||
@ -21,14 +21,15 @@ defmodule Mix.Tasks.SetModerator do
|
||||
end
|
||||
|
||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||
info =
|
||||
user.info
|
||||
|> Map.put("is_moderator", !!moderator)
|
||||
info_cng = User.Info.admin_api_update(user.info, %{is_moderator: !!moderator})
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
{:ok, user} = User.update_and_set_cache(cng)
|
||||
user_cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}")
|
||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||
|
||||
IO.puts("Moderator status of #{nickname}: #{user.info.is_moderator}")
|
||||
else
|
||||
_ ->
|
||||
IO.puts("No local user #{nickname}")
|
||||
|
97
lib/mix/tasks/migrate_local_uploads.ex
Normal file
97
lib/mix/tasks/migrate_local_uploads.ex
Normal file
@ -0,0 +1,97 @@
|
||||
defmodule Mix.Tasks.MigrateLocalUploads do
|
||||
use Mix.Task
|
||||
import Mix.Ecto
|
||||
alias Pleroma.{Upload, Uploaders.Local, Uploaders.S3}
|
||||
require Logger
|
||||
|
||||
@log_every 50
|
||||
@shortdoc "Migrate uploads from local to remote storage"
|
||||
|
||||
def run([target_uploader | args]) do
|
||||
delete? = Enum.member?(args, "--delete")
|
||||
Application.ensure_all_started(:pleroma)
|
||||
|
||||
local_path = Pleroma.Config.get!([Local, :uploads])
|
||||
uploader = Module.concat(Pleroma.Uploaders, target_uploader)
|
||||
|
||||
unless Code.ensure_loaded?(uploader) do
|
||||
raise("The uploader #{inspect(uploader)} is not an existing/loaded module.")
|
||||
end
|
||||
|
||||
target_enabled? = Pleroma.Config.get([Upload, :uploader]) == uploader
|
||||
|
||||
unless target_enabled? do
|
||||
Pleroma.Config.put([Upload, :uploader], uploader)
|
||||
end
|
||||
|
||||
Logger.info("Migrating files from local #{local_path} to #{to_string(uploader)}")
|
||||
|
||||
if delete? do
|
||||
Logger.warn(
|
||||
"Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"
|
||||
)
|
||||
|
||||
:timer.sleep(:timer.seconds(5))
|
||||
end
|
||||
|
||||
uploads =
|
||||
File.ls!(local_path)
|
||||
|> Enum.map(fn id ->
|
||||
root_path = Path.join(local_path, id)
|
||||
|
||||
cond do
|
||||
File.dir?(root_path) ->
|
||||
files = for file <- File.ls!(root_path), do: {id, file, Path.join([root_path, file])}
|
||||
|
||||
case List.first(files) do
|
||||
{id, file, path} ->
|
||||
{%Pleroma.Upload{id: id, name: file, path: id <> "/" <> file, tempfile: path},
|
||||
root_path}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
||||
File.exists?(root_path) ->
|
||||
file = Path.basename(id)
|
||||
[hash, ext] = String.split(id, ".")
|
||||
{%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path}
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
total_count = length(uploads)
|
||||
Logger.info("Found #{total_count} uploads")
|
||||
|
||||
uploads
|
||||
|> Task.async_stream(
|
||||
fn {upload, root_path} ->
|
||||
case Upload.store(upload, uploader: uploader, filters: [], size_limit: nil) do
|
||||
{:ok, _} ->
|
||||
if delete?, do: File.rm_rf!(root_path)
|
||||
Logger.debug("uploaded: #{inspect(upload.path)} #{inspect(upload)}")
|
||||
:ok
|
||||
|
||||
error ->
|
||||
Logger.error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
|
||||
end
|
||||
end,
|
||||
timeout: 150_000
|
||||
)
|
||||
|> Stream.chunk_every(@log_every)
|
||||
|> Enum.reduce(0, fn done, count ->
|
||||
count = count + length(done)
|
||||
Logger.info("Uploaded #{count}/#{total_count} files")
|
||||
count
|
||||
end)
|
||||
|
||||
Logger.info("Done!")
|
||||
end
|
||||
|
||||
def run(_) do
|
||||
Logger.error("Usage: migrate_local_uploads S3|Swift [--delete]")
|
||||
end
|
||||
end
|
@ -4,3 +4,4 @@ CREATE DATABASE pleroma_dev OWNER pleroma;
|
||||
--Extensions made by ecto.migrate that need superuser access
|
||||
CREATE EXTENSION IF NOT EXISTS citext;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
@ -1,5 +1,6 @@
|
||||
defmodule Mix.Tasks.SetAdmin do
|
||||
use Mix.Task
|
||||
import Ecto.Changeset
|
||||
alias Pleroma.User
|
||||
|
||||
@doc """
|
||||
@ -9,21 +10,22 @@ defmodule Mix.Tasks.SetAdmin do
|
||||
def run([nickname | rest]) do
|
||||
Application.ensure_all_started(:pleroma)
|
||||
|
||||
status =
|
||||
admin =
|
||||
case rest do
|
||||
[status] -> status == "true"
|
||||
[admin] -> admin == "true"
|
||||
_ -> true
|
||||
end
|
||||
|
||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||
info =
|
||||
user.info
|
||||
|> Map.put("is_admin", !!status)
|
||||
info_cng = User.Info.admin_api_update(user.info, %{is_admin: !!admin})
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
{:ok, user} = User.update_and_set_cache(cng)
|
||||
user_cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}")
|
||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||
|
||||
IO.puts("Admin status of #{nickname}: #{user.info.is_admin}")
|
||||
else
|
||||
_ ->
|
||||
IO.puts("No local user #{nickname}")
|
||||
|
@ -10,11 +10,11 @@ defmodule Mix.Tasks.SetLocked do
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
import Mix.Ecto
|
||||
import Ecto.Changeset
|
||||
alias Pleroma.{Repo, User}
|
||||
|
||||
def run([nickname | rest]) do
|
||||
ensure_started(Repo, [])
|
||||
Application.ensure_all_started(:pleroma)
|
||||
|
||||
locked =
|
||||
case rest do
|
||||
@ -23,14 +23,15 @@ defmodule Mix.Tasks.SetLocked do
|
||||
end
|
||||
|
||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||
info =
|
||||
user.info
|
||||
|> Map.put("locked", !!locked)
|
||||
info_cng = User.Info.profile_update(user.info, %{locked: !!locked})
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
user = Repo.update!(cng)
|
||||
user_cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
IO.puts("locked status of #{nickname}: #{user.info["locked"]}")
|
||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||
|
||||
IO.puts("Locked status of #{nickname}: #{user.info.locked}")
|
||||
else
|
||||
_ ->
|
||||
IO.puts("No local user #{nickname}")
|
||||
|
@ -1,5 +1,6 @@
|
||||
defmodule Pleroma.Application do
|
||||
use Application
|
||||
import Supervisor.Spec
|
||||
|
||||
@name "Pleroma"
|
||||
@version Mix.Project.config()[:version]
|
||||
@ -7,11 +8,15 @@ defmodule Pleroma.Application do
|
||||
def version, do: @version
|
||||
def named_version(), do: @name <> " " <> @version
|
||||
|
||||
def user_agent() do
|
||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
end
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
@env Mix.env()
|
||||
def start(_type, _args) do
|
||||
import Supervisor.Spec
|
||||
import Cachex.Spec
|
||||
|
||||
# Define workers and child supervisors to be supervised
|
||||
@ -20,10 +25,6 @@ defmodule Pleroma.Application do
|
||||
# Start the Ecto repository
|
||||
supervisor(Pleroma.Repo, []),
|
||||
worker(Pleroma.Emoji, []),
|
||||
# Start the endpoint when the application starts
|
||||
supervisor(Pleroma.Web.Endpoint, []),
|
||||
# Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3)
|
||||
# worker(Pleroma.Worker, [arg1, arg2, arg3]),
|
||||
worker(
|
||||
Cachex,
|
||||
[
|
||||
@ -63,21 +64,18 @@ defmodule Pleroma.Application do
|
||||
],
|
||||
id: :cachex_idem
|
||||
),
|
||||
worker(Pleroma.Web.Federator, []),
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Gopher.Server, []),
|
||||
worker(Pleroma.Web.Federator, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Pleroma.Web.Push, [])
|
||||
] ++
|
||||
if @env == :test,
|
||||
do: [],
|
||||
else:
|
||||
[worker(Pleroma.Web.Streamer, [])] ++
|
||||
if(
|
||||
!chat_enabled(),
|
||||
do: [],
|
||||
else: [worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
||||
)
|
||||
streamer_child() ++
|
||||
chat_child() ++
|
||||
[
|
||||
# Start the endpoint when the application starts
|
||||
supervisor(Pleroma.Web.Endpoint, []),
|
||||
worker(Pleroma.Gopher.Server, [])
|
||||
]
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
@ -85,7 +83,20 @@ defmodule Pleroma.Application do
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
defp chat_enabled do
|
||||
Application.get_env(:pleroma, :chat, []) |> Keyword.get(:enabled)
|
||||
if Mix.env() == :test do
|
||||
defp streamer_child(), do: []
|
||||
defp chat_child(), do: []
|
||||
else
|
||||
defp streamer_child() do
|
||||
[worker(Pleroma.Web.Streamer, [])]
|
||||
end
|
||||
|
||||
defp chat_child() do
|
||||
if Pleroma.Config.get([:chat, :enabled]) do
|
||||
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -39,4 +39,18 @@ defmodule Pleroma.Config do
|
||||
def put(key, value) do
|
||||
Application.put_env(:pleroma, key, value)
|
||||
end
|
||||
|
||||
def delete([key]), do: delete(key)
|
||||
|
||||
def delete([parent_key | keys]) do
|
||||
{_, parent} =
|
||||
Application.get_env(:pleroma, parent_key)
|
||||
|> get_and_update_in(keys, fn _ -> :pop end)
|
||||
|
||||
Application.put_env(:pleroma, parent_key, parent)
|
||||
end
|
||||
|
||||
def delete(key) do
|
||||
Application.delete_env(:pleroma, key)
|
||||
end
|
||||
end
|
||||
|
@ -114,10 +114,10 @@ defmodule Pleroma.Formatter do
|
||||
|
||||
subs =
|
||||
subs ++
|
||||
Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} ->
|
||||
Enum.map(mentions, fn {match, %User{id: id, ap_id: ap_id, info: info}, uuid} ->
|
||||
ap_id =
|
||||
if is_binary(info["source_data"]["url"]) do
|
||||
info["source_data"]["url"]
|
||||
if is_binary(info.source_data["url"]) do
|
||||
info.source_data["url"]
|
||||
else
|
||||
ap_id
|
||||
end
|
||||
@ -125,7 +125,7 @@ defmodule Pleroma.Formatter do
|
||||
short_match = String.split(match, "@") |> tl() |> hd()
|
||||
|
||||
{uuid,
|
||||
"<span><a class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
|
||||
"<span><a data-user='#{id}' class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
|
||||
end)
|
||||
|
||||
{subs, uuid_text}
|
||||
@ -147,7 +147,11 @@ defmodule Pleroma.Formatter do
|
||||
subs =
|
||||
subs ++
|
||||
Enum.map(tags, fn {tag_text, tag, uuid} ->
|
||||
url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag_text}</a>"
|
||||
url =
|
||||
"<a data-tag='#{tag}' href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{
|
||||
tag_text
|
||||
}</a>"
|
||||
|
||||
{uuid, url}
|
||||
end)
|
||||
|
||||
|
@ -6,27 +6,28 @@ defmodule Pleroma.Gopher.Server do
|
||||
config = Pleroma.Config.get(:gopher, [])
|
||||
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
|
||||
port = Keyword.get(config, :port, 1234)
|
||||
GenServer.start_link(__MODULE__, [ip, port], [])
|
||||
|
||||
if Keyword.get(config, :enabled, false) do
|
||||
GenServer.start_link(__MODULE__, [ip, port], [])
|
||||
else
|
||||
Logger.info("Gopher server disabled")
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def init([ip, port]) do
|
||||
if Pleroma.Config.get([:gopher, :enabled], false) do
|
||||
Logger.info("Starting gopher server on #{port}")
|
||||
Logger.info("Starting gopher server on #{port}")
|
||||
|
||||
:ranch.start_listener(
|
||||
:gopher,
|
||||
100,
|
||||
:ranch_tcp,
|
||||
[port: port],
|
||||
__MODULE__.ProtocolHandler,
|
||||
[]
|
||||
)
|
||||
:ranch.start_listener(
|
||||
:gopher,
|
||||
100,
|
||||
:ranch_tcp,
|
||||
[port: port],
|
||||
__MODULE__.ProtocolHandler,
|
||||
[]
|
||||
)
|
||||
|
||||
{:ok, %{ip: ip, port: port}}
|
||||
else
|
||||
Logger.info("Gopher server disabled")
|
||||
{:ok, nil}
|
||||
end
|
||||
{:ok, %{ip: ip, port: port}}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -45,7 +45,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||
|
||||
# paragraphs and linebreaks
|
||||
@ -86,7 +86,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||
Meta.remove_cdata_sections_before_scrub()
|
||||
Meta.strip_comments()
|
||||
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
||||
|
27
lib/pleroma/http/connection.ex
Normal file
27
lib/pleroma/http/connection.ex
Normal file
@ -0,0 +1,27 @@
|
||||
defmodule Pleroma.HTTP.Connection do
|
||||
@moduledoc """
|
||||
Connection for http-requests.
|
||||
"""
|
||||
|
||||
@hackney_options [pool: :default]
|
||||
@adapter Application.get_env(:tesla, :adapter)
|
||||
|
||||
@doc """
|
||||
Configure a client connection
|
||||
|
||||
# Returns
|
||||
|
||||
Tesla.Env.client
|
||||
"""
|
||||
@spec new(Keyword.t()) :: Tesla.Env.client()
|
||||
def new(opts \\ []) do
|
||||
Tesla.client([], {@adapter, hackney_options(opts)})
|
||||
end
|
||||
|
||||
# fetch Hackney options
|
||||
#
|
||||
defp hackney_options(opts \\ []) do
|
||||
options = Keyword.get(opts, :adapter, [])
|
||||
@hackney_options ++ options
|
||||
end
|
||||
end
|
@ -1,14 +1,42 @@
|
||||
defmodule Pleroma.HTTP do
|
||||
require HTTPoison
|
||||
@moduledoc """
|
||||
|
||||
"""
|
||||
|
||||
alias Pleroma.HTTP.Connection
|
||||
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
||||
|
||||
@doc """
|
||||
Builds and perform http request.
|
||||
|
||||
# Arguments:
|
||||
`method` - :get, :post, :put, :delete
|
||||
`url`
|
||||
`body`
|
||||
`headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
|
||||
`options` - custom, per-request middleware or adapter options
|
||||
|
||||
# Returns:
|
||||
`{:ok, %Tesla.Env{}}` or `{:error, error}`
|
||||
|
||||
"""
|
||||
def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
||||
options =
|
||||
process_request_options(options)
|
||||
|> process_sni_options(url)
|
||||
|
||||
HTTPoison.request(method, url, body, headers, options)
|
||||
%{}
|
||||
|> Builder.method(method)
|
||||
|> Builder.headers(headers)
|
||||
|> Builder.opts(options)
|
||||
|> Builder.url(url)
|
||||
|> Builder.add_param(:body, :body, body)
|
||||
|> Enum.into([])
|
||||
|> (&Tesla.request(Connection.new(), &1)).()
|
||||
end
|
||||
|
||||
defp process_sni_options(options, nil), do: options
|
||||
|
||||
defp process_sni_options(options, url) do
|
||||
uri = URI.parse(url)
|
||||
host = uri.host |> to_charlist()
|
||||
@ -22,7 +50,7 @@ defmodule Pleroma.HTTP do
|
||||
def process_request_options(options) do
|
||||
config = Application.get_env(:pleroma, :http, [])
|
||||
proxy = Keyword.get(config, :proxy_url, nil)
|
||||
options = options ++ [hackney: [pool: :default]]
|
||||
options = options ++ [adapter: [pool: :default]]
|
||||
|
||||
case proxy do
|
||||
nil -> options
|
||||
@ -30,8 +58,19 @@ defmodule Pleroma.HTTP do
|
||||
end
|
||||
end
|
||||
|
||||
def get(url, headers \\ [], options \\ []), do: request(:get, url, "", headers, options)
|
||||
@doc """
|
||||
Performs GET request.
|
||||
|
||||
See `Pleroma.HTTP.request/5`
|
||||
"""
|
||||
def get(url, headers \\ [], options \\ []),
|
||||
do: request(:get, url, "", headers, options)
|
||||
|
||||
@doc """
|
||||
Performs POST request.
|
||||
|
||||
See `Pleroma.HTTP.request/5`
|
||||
"""
|
||||
def post(url, body, headers \\ [], options \\ []),
|
||||
do: request(:post, url, body, headers, options)
|
||||
end
|
||||
|
126
lib/pleroma/http/request_builder.ex
Normal file
126
lib/pleroma/http/request_builder.ex
Normal file
@ -0,0 +1,126 @@
|
||||
defmodule Pleroma.HTTP.RequestBuilder do
|
||||
@moduledoc """
|
||||
Helper functions for building Tesla requests
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Specify the request method when building a request
|
||||
|
||||
## Parameters
|
||||
|
||||
- request (Map) - Collected request options
|
||||
- m (atom) - Request method
|
||||
|
||||
## Returns
|
||||
|
||||
Map
|
||||
"""
|
||||
@spec method(map(), atom) :: map()
|
||||
def method(request, m) do
|
||||
Map.put_new(request, :method, m)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Specify the request method when building a request
|
||||
|
||||
## Parameters
|
||||
|
||||
- request (Map) - Collected request options
|
||||
- u (String) - Request URL
|
||||
|
||||
## Returns
|
||||
|
||||
Map
|
||||
"""
|
||||
@spec url(map(), String.t()) :: map()
|
||||
def url(request, u) do
|
||||
Map.put_new(request, :url, u)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Add headers to the request
|
||||
"""
|
||||
@spec headers(map(), list(tuple)) :: map()
|
||||
def headers(request, h) do
|
||||
Map.put_new(request, :headers, h)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Add custom, per-request middleware or adapter options to the request
|
||||
"""
|
||||
@spec opts(map(), Keyword.t()) :: map()
|
||||
def opts(request, options) do
|
||||
Map.put_new(request, :opts, options)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Add optional parameters to the request
|
||||
|
||||
## Parameters
|
||||
|
||||
- request (Map) - Collected request options
|
||||
- definitions (Map) - Map of parameter name to parameter location.
|
||||
- options (KeywordList) - The provided optional parameters
|
||||
|
||||
## Returns
|
||||
|
||||
Map
|
||||
"""
|
||||
@spec add_optional_params(map(), %{optional(atom) => atom}, keyword()) :: map()
|
||||
def add_optional_params(request, _, []), do: request
|
||||
|
||||
def add_optional_params(request, definitions, [{key, value} | tail]) do
|
||||
case definitions do
|
||||
%{^key => location} ->
|
||||
request
|
||||
|> add_param(location, key, value)
|
||||
|> add_optional_params(definitions, tail)
|
||||
|
||||
_ ->
|
||||
add_optional_params(request, definitions, tail)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Add optional parameters to the request
|
||||
|
||||
## Parameters
|
||||
|
||||
- request (Map) - Collected request options
|
||||
- location (atom) - Where to put the parameter
|
||||
- key (atom) - The name of the parameter
|
||||
- value (any) - The value of the parameter
|
||||
|
||||
## Returns
|
||||
|
||||
Map
|
||||
"""
|
||||
@spec add_param(map(), atom, atom, any()) :: map()
|
||||
def add_param(request, :body, :body, value), do: Map.put(request, :body, value)
|
||||
|
||||
def add_param(request, :body, key, value) do
|
||||
request
|
||||
|> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
|
||||
|> Map.update!(
|
||||
:body,
|
||||
&Tesla.Multipart.add_field(&1, key, Poison.encode!(value),
|
||||
headers: [{:"Content-Type", "application/json"}]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def add_param(request, :file, name, path) do
|
||||
request
|
||||
|> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
|
||||
|> Map.update!(:body, &Tesla.Multipart.add_file(&1, path, name: name))
|
||||
end
|
||||
|
||||
def add_param(request, :form, name, value) do
|
||||
request
|
||||
|> Map.update(:body, %{name => value}, &Map.put(&1, name, value))
|
||||
end
|
||||
|
||||
def add_param(request, location, key, value) do
|
||||
Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}]))
|
||||
end
|
||||
end
|
108
lib/pleroma/mime.ex
Normal file
108
lib/pleroma/mime.ex
Normal file
@ -0,0 +1,108 @@
|
||||
defmodule Pleroma.MIME do
|
||||
@moduledoc """
|
||||
Returns the mime-type of a binary and optionally a normalized file-name.
|
||||
"""
|
||||
@default "application/octet-stream"
|
||||
@read_bytes 31
|
||||
|
||||
@spec file_mime_type(String.t()) ::
|
||||
{:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error
|
||||
def file_mime_type(path, filename) do
|
||||
with {:ok, content_type} <- file_mime_type(path),
|
||||
filename <- fix_extension(filename, content_type) do
|
||||
{:ok, content_type, filename}
|
||||
end
|
||||
end
|
||||
|
||||
@spec file_mime_type(String.t()) :: {:ok, String.t()} | {:error, any()} | :error
|
||||
def file_mime_type(filename) do
|
||||
File.open(filename, [:read], fn f ->
|
||||
check_mime_type(IO.binread(f, @read_bytes))
|
||||
end)
|
||||
end
|
||||
|
||||
def bin_mime_type(binary, filename) do
|
||||
with {:ok, content_type} <- bin_mime_type(binary),
|
||||
filename <- fix_extension(filename, content_type) do
|
||||
{:ok, content_type, filename}
|
||||
end
|
||||
end
|
||||
|
||||
@spec bin_mime_type(binary()) :: {:ok, String.t()} | :error
|
||||
def bin_mime_type(<<head::binary-size(@read_bytes), _::binary>>) do
|
||||
{:ok, check_mime_type(head)}
|
||||
end
|
||||
|
||||
def mime_type(<<_::binary>>), do: {:ok, @default}
|
||||
|
||||
def bin_mime_type(_), do: :error
|
||||
|
||||
defp fix_extension(filename, content_type) do
|
||||
parts = String.split(filename, ".")
|
||||
|
||||
new_filename =
|
||||
if length(parts) > 1 do
|
||||
Enum.drop(parts, -1) |> Enum.join(".")
|
||||
else
|
||||
Enum.join(parts)
|
||||
end
|
||||
|
||||
cond do
|
||||
content_type == "application/octet-stream" ->
|
||||
filename
|
||||
|
||||
ext = List.first(MIME.extensions(content_type)) ->
|
||||
new_filename <> "." <> ext
|
||||
|
||||
true ->
|
||||
Enum.join([new_filename, String.split(content_type, "/") |> List.last()], ".")
|
||||
end
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, _::binary>>) do
|
||||
"image/png"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x47, 0x49, 0x46, 0x38, _, 0x61, _::binary>>) do
|
||||
"image/gif"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0xFF, 0xD8, 0xFF, _::binary>>) do
|
||||
"image/jpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x1A, 0x45, 0xDF, 0xA3, _::binary>>) do
|
||||
"video/webm"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70, _::binary>>) do
|
||||
"video/mp4"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x49, 0x44, 0x33, _::binary>>) do
|
||||
"audio/mpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<255, 251, _, 68, 0, 0, 0, 0, _::binary>>) do
|
||||
"audio/mpeg"
|
||||
end
|
||||
|
||||
defp check_mime_type(
|
||||
<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::size(160), 0x80, 0x74, 0x68, 0x65,
|
||||
0x6F, 0x72, 0x61, _::binary>>
|
||||
) do
|
||||
"video/ogg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::binary>>) do
|
||||
"audio/ogg"
|
||||
end
|
||||
|
||||
defp check_mime_type(<<0x52, 0x49, 0x46, 0x46, _::binary>>) do
|
||||
"audio/wav"
|
||||
end
|
||||
|
||||
defp check_mime_type(_) do
|
||||
@default
|
||||
end
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
defmodule Pleroma.Object do
|
||||
use Ecto.Schema
|
||||
alias Pleroma.{Repo, Object, Activity}
|
||||
alias Pleroma.{Repo, Object, User, Activity}
|
||||
import Ecto.{Query, Changeset}
|
||||
|
||||
schema "objects" do
|
||||
@ -31,6 +31,13 @@ defmodule Pleroma.Object do
|
||||
def normalize(ap_id) when is_binary(ap_id), do: Object.get_by_ap_id(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
# Owned objects can only be mutated by their owner
|
||||
def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
|
||||
do: actor == ap_id
|
||||
|
||||
# Legacy objects can be mutated by anybody
|
||||
def authorize_mutation(%Object{}, %User{}), do: true
|
||||
|
||||
if Mix.env() == :test do
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
get_by_ap_id(ap_id)
|
||||
|
@ -1,26 +1,22 @@
|
||||
defmodule Pleroma.Plugs.OAuthPlug do
|
||||
import Plug.Conn
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
import Ecto.Query
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
alias Pleroma.{
|
||||
User,
|
||||
Repo,
|
||||
Web.OAuth.Token
|
||||
}
|
||||
|
||||
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
||||
|
||||
def init(options), do: options
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(conn, _) do
|
||||
token =
|
||||
case get_req_header(conn, "authorization") do
|
||||
["Bearer " <> header] -> header
|
||||
_ -> get_session(conn, :oauth_token)
|
||||
end
|
||||
|
||||
with token when not is_nil(token) <- token,
|
||||
%Token{user_id: user_id} = token <- Repo.get_by(Token, token: token),
|
||||
%User{} = user <- Repo.get(User, user_id),
|
||||
false <- !!user.info["deactivated"] do
|
||||
with {:ok, token} <- fetch_token(conn),
|
||||
{:ok, user} <- fetch_user(token) do
|
||||
conn
|
||||
|> assign(:token, token)
|
||||
|> assign(:user, user)
|
||||
@ -28,4 +24,47 @@ defmodule Pleroma.Plugs.OAuthPlug do
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
|
||||
# Gets user by token
|
||||
#
|
||||
@spec fetch_user(String.t()) :: {:ok, User.t()} | nil
|
||||
defp fetch_user(token) do
|
||||
query = from(q in Token, where: q.token == ^token, preload: [:user])
|
||||
|
||||
with %Token{user: %{info: %{deactivated: false} = _} = user} <- Repo.one(query) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
# Gets token from session by :oauth_token key
|
||||
#
|
||||
@spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_from_session(conn) do
|
||||
case get_session(conn, :oauth_token) do
|
||||
nil -> :no_token_found
|
||||
token -> {:ok, token}
|
||||
end
|
||||
end
|
||||
|
||||
# Gets token from headers
|
||||
#
|
||||
@spec fetch_token(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token(%Plug.Conn{} = conn) do
|
||||
headers = get_req_header(conn, "authorization")
|
||||
|
||||
with :no_token_found <- fetch_token(headers),
|
||||
do: fetch_token_from_session(conn)
|
||||
end
|
||||
|
||||
@spec fetch_token(Keyword.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token([]), do: :no_token_found
|
||||
|
||||
defp fetch_token([token | tail]) do
|
||||
trimmed_token = String.trim(token)
|
||||
|
||||
case Regex.run(@realm_reg, trimmed_token) do
|
||||
[_, match] -> {:ok, String.trim(match)}
|
||||
_ -> fetch_token(tail)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
78
lib/pleroma/plugs/uploaded_media.ex
Normal file
78
lib/pleroma/plugs/uploaded_media.ex
Normal file
@ -0,0 +1,78 @@
|
||||
defmodule Pleroma.Plugs.UploadedMedia do
|
||||
@moduledoc """
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
require Logger
|
||||
|
||||
@behaviour Plug
|
||||
# no slashes
|
||||
@path "media"
|
||||
@cache_control %{
|
||||
default: "public, max-age=1209600",
|
||||
error: "public, must-revalidate, max-age=160"
|
||||
}
|
||||
|
||||
def init(_opts) do
|
||||
static_plug_opts =
|
||||
[]
|
||||
|> Keyword.put(:from, "__unconfigured_media_plug")
|
||||
|> Keyword.put(:at, "/__unconfigured_media_plug")
|
||||
|> Plug.Static.init()
|
||||
|
||||
%{static_plug_opts: static_plug_opts}
|
||||
end
|
||||
|
||||
def call(conn = %{request_path: <<"/", @path, "/", file::binary>>}, opts) do
|
||||
config = Pleroma.Config.get([Pleroma.Upload])
|
||||
|
||||
with uploader <- Keyword.fetch!(config, :uploader),
|
||||
proxy_remote = Keyword.get(config, :proxy_remote, false),
|
||||
{:ok, get_method} <- uploader.get_file(file) do
|
||||
get_media(conn, get_method, proxy_remote, opts)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(500, "Failed")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _opts), do: conn
|
||||
|
||||
defp get_media(conn, {:static_dir, directory}, _, opts) do
|
||||
static_opts =
|
||||
Map.get(opts, :static_plug_opts)
|
||||
|> Map.put(:at, [@path])
|
||||
|> Map.put(:from, directory)
|
||||
|
||||
conn = Plug.Static.call(conn, static_opts)
|
||||
|
||||
if conn.halted do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> send_resp(404, "Not found")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp get_media(conn, {:url, url}, true, _) do
|
||||
conn
|
||||
|> Pleroma.ReverseProxy.call(url, Pleroma.Config.get([Pleroma.Upload, :proxy_opts], []))
|
||||
end
|
||||
|
||||
defp get_media(conn, {:url, url}, _, _) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: url)
|
||||
|> halt()
|
||||
end
|
||||
|
||||
defp get_media(conn, unknown, _, _) do
|
||||
Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
|
||||
|
||||
conn
|
||||
|> send_resp(500, "Internal Error")
|
||||
|> halt()
|
||||
end
|
||||
end
|
@ -6,7 +6,7 @@ defmodule Pleroma.Plugs.UserEnabledPlug do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{info: %{"deactivated" => true}}}} = conn, _) do
|
||||
def call(%{assigns: %{user: %User{info: %{deactivated: true}}}} = conn, _) do
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
end
|
||||
|
@ -6,7 +6,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{info: %{"is_admin" => true}}}} = conn, _) do
|
||||
def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do
|
||||
conn
|
||||
end
|
||||
|
||||
|
343
lib/pleroma/reverse_proxy.ex
Normal file
343
lib/pleroma/reverse_proxy.ex
Normal file
@ -0,0 +1,343 @@
|
||||
defmodule Pleroma.ReverseProxy do
|
||||
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since if-unmodified-since if-none-match if-range range)
|
||||
@resp_cache_headers ~w(etag date last-modified cache-control)
|
||||
@keep_resp_headers @resp_cache_headers ++
|
||||
~w(content-type content-disposition content-encoding content-range accept-ranges vary)
|
||||
@default_cache_control_header "public, max-age=1209600"
|
||||
@valid_resp_codes [200, 206, 304]
|
||||
@max_read_duration :timer.seconds(30)
|
||||
@max_body_length :infinity
|
||||
@methods ~w(GET HEAD)
|
||||
|
||||
@moduledoc """
|
||||
A reverse proxy.
|
||||
|
||||
Pleroma.ReverseProxy.call(conn, url, options)
|
||||
|
||||
It is not meant to be added into a plug pipeline, but to be called from another plug or controller.
|
||||
|
||||
Supports `#{inspect(@methods)}` HTTP methods, and only allows `#{inspect(@valid_resp_codes)}` status codes.
|
||||
|
||||
Responses are chunked to the client while downloading from the upstream.
|
||||
|
||||
Some request / responses headers are preserved:
|
||||
|
||||
* request: `#{inspect(@keep_req_headers)}`
|
||||
* response: `#{inspect(@keep_resp_headers)}`
|
||||
|
||||
If no caching headers (`#{inspect(@resp_cache_headers)}`) are returned by upstream, `cache-control` will be
|
||||
set to `#{inspect(@default_cache_control_header)}`.
|
||||
|
||||
Options:
|
||||
|
||||
* `redirect_on_failure` (default `false`). Redirects the client to the real remote URL if there's any HTTP
|
||||
errors. Any error during body processing will not be redirected as the response is chunked. This may expose
|
||||
remote URL, clients IPs, ….
|
||||
|
||||
* `max_body_length` (default `#{inspect(@max_body_length)}`): limits the content length to be approximately the
|
||||
specified length. It is validated with the `content-length` header and also verified when proxying.
|
||||
|
||||
* `max_read_duration` (default `#{inspect(@max_read_duration)}` ms): the total time the connection is allowed to
|
||||
read from the remote upstream.
|
||||
|
||||
* `inline_content_types`:
|
||||
* `true` will not alter `content-disposition` (up to the upstream),
|
||||
* `false` will add `content-disposition: attachment` to any request,
|
||||
* a list of whitelisted content types
|
||||
|
||||
* `keep_user_agent` will forward the client's user-agent to the upstream. This may be useful if the upstream is
|
||||
doing content transformation (encoding, …) depending on the request.
|
||||
|
||||
* `req_headers`, `resp_headers` additional headers.
|
||||
|
||||
* `http`: options for [hackney](https://github.com/benoitc/hackney).
|
||||
|
||||
"""
|
||||
@hackney Application.get_env(:pleroma, :hackney, :hackney)
|
||||
@httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
|
||||
|
||||
@default_hackney_options [{:follow_redirect, true}]
|
||||
|
||||
@inline_content_types [
|
||||
"image/gif",
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
"audio/mpeg",
|
||||
"audio/mp3",
|
||||
"video/webm",
|
||||
"video/mp4",
|
||||
"video/quicktime"
|
||||
]
|
||||
|
||||
require Logger
|
||||
import Plug.Conn
|
||||
|
||||
@type option() ::
|
||||
{:keep_user_agent, boolean}
|
||||
| {:max_read_duration, :timer.time() | :infinity}
|
||||
| {:max_body_length, non_neg_integer() | :infinity}
|
||||
| {:http, []}
|
||||
| {:req_headers, [{String.t(), String.t()}]}
|
||||
| {:resp_headers, [{String.t(), String.t()}]}
|
||||
| {:inline_content_types, boolean() | [String.t()]}
|
||||
| {:redirect_on_failure, boolean()}
|
||||
|
||||
@spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t()
|
||||
def call(conn = %{method: method}, url, opts \\ []) when method in @methods do
|
||||
hackney_opts =
|
||||
@default_hackney_options
|
||||
|> Keyword.merge(Keyword.get(opts, :http, []))
|
||||
|> @httpoison.process_request_options()
|
||||
|
||||
req_headers = build_req_headers(conn.req_headers, opts)
|
||||
|
||||
opts =
|
||||
if filename = Pleroma.Web.MediaProxy.filename(url) do
|
||||
Keyword.put_new(opts, :attachment_name, filename)
|
||||
else
|
||||
opts
|
||||
end
|
||||
|
||||
with {:ok, code, headers, client} <- request(method, url, req_headers, hackney_opts),
|
||||
:ok <- header_length_constraint(headers, Keyword.get(opts, :max_body_length)) do
|
||||
response(conn, client, url, code, headers, opts)
|
||||
else
|
||||
{:ok, code, headers} ->
|
||||
head_response(conn, url, code, headers, opts)
|
||||
|> halt()
|
||||
|
||||
{:error, {:invalid_http_response, code}} ->
|
||||
Logger.error("#{__MODULE__}: request to #{inspect(url)} failed with HTTP status #{code}")
|
||||
|
||||
conn
|
||||
|> error_or_redirect(
|
||||
url,
|
||||
code,
|
||||
"Request failed: " <> Plug.Conn.Status.reason_phrase(code),
|
||||
opts
|
||||
)
|
||||
|> halt()
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__}: request to #{inspect(url)} failed: #{inspect(error)}")
|
||||
|
||||
conn
|
||||
|> error_or_redirect(url, 500, "Request failed", opts)
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _, _) do
|
||||
conn
|
||||
|> send_resp(400, Plug.Conn.Status.reason_phrase(400))
|
||||
|> halt()
|
||||
end
|
||||
|
||||
defp request(method, url, headers, hackney_opts) do
|
||||
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
|
||||
method = method |> String.downcase() |> String.to_existing_atom()
|
||||
|
||||
case @hackney.request(method, url, headers, "", hackney_opts) do
|
||||
{:ok, code, headers, client} when code in @valid_resp_codes ->
|
||||
{:ok, code, downcase_headers(headers), client}
|
||||
|
||||
{:ok, code, headers} when code in @valid_resp_codes ->
|
||||
{:ok, code, downcase_headers(headers)}
|
||||
|
||||
{:ok, code, _, _} ->
|
||||
{:error, {:invalid_http_response, code}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp response(conn, client, url, status, headers, opts) do
|
||||
result =
|
||||
conn
|
||||
|> put_resp_headers(build_resp_headers(headers, opts))
|
||||
|> send_chunked(status)
|
||||
|> chunk_reply(client, opts)
|
||||
|
||||
case result do
|
||||
{:ok, conn} ->
|
||||
halt(conn)
|
||||
|
||||
{:error, :closed, conn} ->
|
||||
:hackney.close(client)
|
||||
halt(conn)
|
||||
|
||||
{:error, error, conn} ->
|
||||
Logger.warn(
|
||||
"#{__MODULE__} request to #{url} failed while reading/chunking: #{inspect(error)}"
|
||||
)
|
||||
|
||||
:hackney.close(client)
|
||||
halt(conn)
|
||||
end
|
||||
end
|
||||
|
||||
defp chunk_reply(conn, client, opts) do
|
||||
chunk_reply(conn, client, opts, 0, 0)
|
||||
end
|
||||
|
||||
defp chunk_reply(conn, client, opts, sent_so_far, duration) do
|
||||
with {:ok, duration} <-
|
||||
check_read_duration(
|
||||
duration,
|
||||
Keyword.get(opts, :max_read_duration, @max_read_duration)
|
||||
),
|
||||
{:ok, data} <- @hackney.stream_body(client),
|
||||
{:ok, duration} <- increase_read_duration(duration),
|
||||
sent_so_far = sent_so_far + byte_size(data),
|
||||
:ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
|
||||
{:ok, conn} <- chunk(conn, data) do
|
||||
chunk_reply(conn, client, opts, sent_so_far, duration)
|
||||
else
|
||||
:done -> {:ok, conn}
|
||||
{:error, error} -> {:error, error, conn}
|
||||
end
|
||||
end
|
||||
|
||||
defp head_response(conn, _url, code, headers, opts) do
|
||||
conn
|
||||
|> put_resp_headers(build_resp_headers(headers, opts))
|
||||
|> send_resp(code, "")
|
||||
end
|
||||
|
||||
defp error_or_redirect(conn, url, code, body, opts) do
|
||||
if Keyword.get(opts, :redirect_on_failure, false) do
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: url)
|
||||
|> halt()
|
||||
else
|
||||
conn
|
||||
|> send_resp(code, body)
|
||||
|> halt
|
||||
end
|
||||
end
|
||||
|
||||
defp downcase_headers(headers) do
|
||||
Enum.map(headers, fn {k, v} ->
|
||||
{String.downcase(k), v}
|
||||
end)
|
||||
end
|
||||
|
||||
defp get_content_type(headers) do
|
||||
{_, content_type} =
|
||||
List.keyfind(headers, "content-type", 0, {"content-type", "application/octet-stream"})
|
||||
|
||||
[content_type | _] = String.split(content_type, ";")
|
||||
content_type
|
||||
end
|
||||
|
||||
defp put_resp_headers(conn, headers) do
|
||||
Enum.reduce(headers, conn, fn {k, v}, conn ->
|
||||
put_resp_header(conn, k, v)
|
||||
end)
|
||||
end
|
||||
|
||||
defp build_req_headers(headers, opts) do
|
||||
headers =
|
||||
headers
|
||||
|> downcase_headers()
|
||||
|> Enum.filter(fn {k, _} -> k in @keep_req_headers end)
|
||||
|> (fn headers ->
|
||||
headers = headers ++ Keyword.get(opts, :req_headers, [])
|
||||
|
||||
if Keyword.get(opts, :keep_user_agent, false) do
|
||||
List.keystore(
|
||||
headers,
|
||||
"user-agent",
|
||||
0,
|
||||
{"user-agent", Pleroma.Application.user_agent()}
|
||||
)
|
||||
else
|
||||
headers
|
||||
end
|
||||
end).()
|
||||
end
|
||||
|
||||
defp build_resp_headers(headers, opts) do
|
||||
headers
|
||||
|> Enum.filter(fn {k, _} -> k in @keep_resp_headers end)
|
||||
|> build_resp_cache_headers(opts)
|
||||
|> build_resp_content_disposition_header(opts)
|
||||
|> (fn headers -> headers ++ Keyword.get(opts, :resp_headers, []) end).()
|
||||
end
|
||||
|
||||
defp build_resp_cache_headers(headers, opts) do
|
||||
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
|
||||
|
||||
if has_cache? do
|
||||
headers
|
||||
else
|
||||
List.keystore(headers, "cache-control", 0, {"cache-control", @default_cache_control_header})
|
||||
end
|
||||
end
|
||||
|
||||
defp build_resp_content_disposition_header(headers, opts) do
|
||||
opt = Keyword.get(opts, :inline_content_types, @inline_content_types)
|
||||
|
||||
content_type = get_content_type(headers)
|
||||
|
||||
attachment? =
|
||||
cond do
|
||||
is_list(opt) && !Enum.member?(opt, content_type) -> true
|
||||
opt == false -> true
|
||||
true -> false
|
||||
end
|
||||
|
||||
if attachment? do
|
||||
disposition = "attachment; filename=" <> Keyword.get(opts, :attachment_name, "attachment")
|
||||
List.keystore(headers, "content-disposition", 0, {"content-disposition", disposition})
|
||||
else
|
||||
headers
|
||||
end
|
||||
end
|
||||
|
||||
defp header_length_constraint(headers, limit) when is_integer(limit) and limit > 0 do
|
||||
with {_, size} <- List.keyfind(headers, "content-length", 0),
|
||||
{size, _} <- Integer.parse(size),
|
||||
true <- size <= limit do
|
||||
:ok
|
||||
else
|
||||
false ->
|
||||
{:error, :body_too_large}
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp header_length_constraint(_, _), do: :ok
|
||||
|
||||
defp body_size_constraint(size, limit) when is_integer(limit) and limit > 0 and size >= limit do
|
||||
{:error, :body_too_large}
|
||||
end
|
||||
|
||||
defp body_size_constraint(_, _), do: :ok
|
||||
|
||||
defp check_read_duration(duration, max)
|
||||
when is_integer(duration) and is_integer(max) and max > 0 do
|
||||
if duration > max do
|
||||
{:error, :read_duration_exceeded}
|
||||
else
|
||||
{:ok, {duration, :erlang.system_time(:millisecond)}}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_read_duration(_, _), do: {:ok, :no_duration_limit, :no_duration_limit}
|
||||
|
||||
defp increase_read_duration({previous_duration, started})
|
||||
when is_integer(previous_duration) and is_integer(started) do
|
||||
duration = :erlang.system_time(:millisecond) - started
|
||||
{:ok, previous_duration + duration}
|
||||
end
|
||||
|
||||
defp increase_read_duration(_) do
|
||||
{:ok, :no_duration_limit, :no_duration_limit}
|
||||
end
|
||||
end
|
@ -1,81 +1,209 @@
|
||||
defmodule Pleroma.Upload do
|
||||
@moduledoc """
|
||||
# Upload
|
||||
|
||||
Options:
|
||||
* `:type`: presets for activity type (defaults to Document) and size limits from app configuration
|
||||
* `:description`: upload alternative text
|
||||
* `:base_url`: override base url
|
||||
* `:uploader`: override uploader
|
||||
* `:filters`: override filters
|
||||
* `:size_limit`: override size limit
|
||||
* `:activity_type`: override activity type
|
||||
|
||||
The `%Pleroma.Upload{}` struct: all documented fields are meant to be overwritten in filters:
|
||||
|
||||
* `:id` - the upload id.
|
||||
* `:name` - the upload file name.
|
||||
* `:path` - the upload path: set at first to `id/name` but can be changed. Keep in mind that the path
|
||||
is once created permanent and changing it (especially in uploaders) is probably a bad idea!
|
||||
* `:tempfile` - path to the temporary file. Prefer in-place changes on the file rather than changing the
|
||||
path as the temporary file is also tracked by `Plug.Upload{}` and automatically deleted once the request is over.
|
||||
|
||||
Related behaviors:
|
||||
|
||||
* `Pleroma.Uploaders.Uploader`
|
||||
* `Pleroma.Upload.Filter`
|
||||
|
||||
"""
|
||||
alias Ecto.UUID
|
||||
require Logger
|
||||
|
||||
def check_file_size(path, nil), do: true
|
||||
@type source ::
|
||||
Plug.Upload.t() | data_uri_string ::
|
||||
String.t() | {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
|
||||
|
||||
def check_file_size(path, size_limit) do
|
||||
{:ok, %{size: size}} = File.stat(path)
|
||||
size <= size_limit
|
||||
end
|
||||
@type option ::
|
||||
{:type, :avatar | :banner | :background}
|
||||
| {:description, String.t()}
|
||||
| {:activity_type, String.t()}
|
||||
| {:size_limit, nil | non_neg_integer()}
|
||||
| {:uploader, module()}
|
||||
| {:filters, [module()]}
|
||||
|
||||
def store(file, should_dedupe, size_limit \\ nil)
|
||||
@type t :: %__MODULE__{
|
||||
id: String.t(),
|
||||
name: String.t(),
|
||||
tempfile: String.t(),
|
||||
content_type: String.t(),
|
||||
path: String.t()
|
||||
}
|
||||
defstruct [:id, :name, :tempfile, :content_type, :path]
|
||||
|
||||
def store(%Plug.Upload{} = file, should_dedupe, size_limit) do
|
||||
content_type = get_content_type(file.path)
|
||||
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
|
||||
def store(upload, opts \\ []) do
|
||||
opts = get_opts(opts)
|
||||
|
||||
with uuid <- get_uuid(file, should_dedupe),
|
||||
name <- get_name(file, uuid, content_type, should_dedupe),
|
||||
true <- check_file_size(file.path, size_limit) do
|
||||
strip_exif_data(content_type, file.path)
|
||||
|
||||
{:ok, url_path} = uploader().put_file(name, uuid, file.path, content_type, should_dedupe)
|
||||
|
||||
%{
|
||||
"type" => "Document",
|
||||
"url" => [
|
||||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => content_type,
|
||||
"href" => url_path
|
||||
}
|
||||
],
|
||||
"name" => name
|
||||
}
|
||||
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),
|
||||
{:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do
|
||||
{:ok,
|
||||
%{
|
||||
"type" => opts.activity_type,
|
||||
"url" => [
|
||||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => upload.content_type,
|
||||
"href" => url_from_spec(opts.base_url, url_spec)
|
||||
}
|
||||
],
|
||||
"name" => Map.get(opts, :description) || upload.name
|
||||
}}
|
||||
else
|
||||
_e -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def store(%{"img" => "data:image/" <> image_data}, should_dedupe, size_limit) do
|
||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
||||
|
||||
with tmp_path <- tempfile_for_image(data),
|
||||
uuid <- UUID.generate(),
|
||||
true <- check_file_size(tmp_path, size_limit) do
|
||||
content_type = get_content_type(tmp_path)
|
||||
strip_exif_data(content_type, tmp_path)
|
||||
|
||||
name =
|
||||
create_name(
|
||||
String.downcase(Base.encode16(:crypto.hash(:sha256, data))),
|
||||
parsed["filetype"],
|
||||
content_type
|
||||
{:error, error} ->
|
||||
Logger.error(
|
||||
"#{__MODULE__} store (using #{inspect(opts.uploader)}) failed: #{inspect(error)}"
|
||||
)
|
||||
|
||||
{:ok, url_path} = uploader().put_file(name, uuid, tmp_path, content_type, should_dedupe)
|
||||
|
||||
%{
|
||||
"type" => "Image",
|
||||
"url" => [
|
||||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => content_type,
|
||||
"href" => url_path
|
||||
}
|
||||
],
|
||||
"name" => name
|
||||
}
|
||||
else
|
||||
_e -> nil
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a tempfile using the Plug.Upload Genserver which cleans them up
|
||||
automatically.
|
||||
"""
|
||||
def tempfile_for_image(data) do
|
||||
defp get_opts(opts) do
|
||||
{size_limit, activity_type} =
|
||||
case Keyword.get(opts, :type) do
|
||||
:banner ->
|
||||
{Pleroma.Config.get!([:instance, :banner_upload_limit]), "Image"}
|
||||
|
||||
:avatar ->
|
||||
{Pleroma.Config.get!([:instance, :avatar_upload_limit]), "Image"}
|
||||
|
||||
:background ->
|
||||
{Pleroma.Config.get!([:instance, :background_upload_limit]), "Image"}
|
||||
|
||||
_ ->
|
||||
{Pleroma.Config.get!([:instance, :upload_limit]), "Document"}
|
||||
end
|
||||
|
||||
opts = %{
|
||||
activity_type: Keyword.get(opts, :activity_type, activity_type),
|
||||
size_limit: Keyword.get(opts, :size_limit, size_limit),
|
||||
uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])),
|
||||
filters: Keyword.get(opts, :filters, Pleroma.Config.get([__MODULE__, :filters])),
|
||||
description: Keyword.get(opts, :description),
|
||||
base_url:
|
||||
Keyword.get(
|
||||
opts,
|
||||
:base_url,
|
||||
Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url())
|
||||
)
|
||||
}
|
||||
|
||||
# TODO: 1.0+ : remove old config compatibility
|
||||
opts =
|
||||
if Pleroma.Config.get([__MODULE__, :strip_exif]) == true &&
|
||||
!Enum.member?(opts.filters, Pleroma.Upload.Filter.Mogrify) do
|
||||
Logger.warn("""
|
||||
Pleroma: configuration `:instance, :strip_exif` is deprecated, please instead set:
|
||||
|
||||
:pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Mogrify]]
|
||||
|
||||
:pleroma, Pleroma.Upload.Filter.Mogrify, args: "strip"
|
||||
""")
|
||||
|
||||
Pleroma.Config.put([Pleroma.Upload.Filter.Mogrify], args: "strip")
|
||||
Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Mogrify])
|
||||
else
|
||||
opts
|
||||
end
|
||||
|
||||
opts =
|
||||
if Pleroma.Config.get([:instance, :dedupe_media]) == true &&
|
||||
!Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do
|
||||
Logger.warn("""
|
||||
Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set:
|
||||
|
||||
:pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]]
|
||||
""")
|
||||
|
||||
Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe])
|
||||
else
|
||||
opts
|
||||
end
|
||||
end
|
||||
|
||||
defp prepare_upload(%Plug.Upload{} = file, opts) do
|
||||
with :ok <- check_file_size(file.path, opts.size_limit),
|
||||
{:ok, content_type, name} <- Pleroma.MIME.file_mime_type(file.path, file.filename) do
|
||||
{:ok,
|
||||
%__MODULE__{
|
||||
id: UUID.generate(),
|
||||
name: name,
|
||||
tempfile: file.path,
|
||||
content_type: content_type
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
||||
defp prepare_upload(%{"img" => "data:image/" <> image_data}, opts) do
|
||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
||||
hash = String.downcase(Base.encode16(:crypto.hash(:sha256, data)))
|
||||
|
||||
with :ok <- check_binary_size(data, opts.size_limit),
|
||||
tmp_path <- tempfile_for_image(data),
|
||||
{:ok, content_type, name} <-
|
||||
Pleroma.MIME.bin_mime_type(data, hash <> "." <> parsed["filetype"]) do
|
||||
{:ok,
|
||||
%__MODULE__{
|
||||
id: UUID.generate(),
|
||||
name: name,
|
||||
tempfile: tmp_path,
|
||||
content_type: content_type
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
||||
# For Mix.Tasks.MigrateLocalUploads
|
||||
defp prepare_upload(upload = %__MODULE__{tempfile: path}, _opts) do
|
||||
with {:ok, content_type} <- Pleroma.MIME.file_mime_type(path) do
|
||||
{:ok, %__MODULE__{upload | content_type: content_type}}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_binary_size(binary, size_limit)
|
||||
when is_integer(size_limit) and size_limit > 0 and byte_size(binary) >= size_limit do
|
||||
{:error, :file_too_large}
|
||||
end
|
||||
|
||||
defp check_binary_size(_, _), do: :ok
|
||||
|
||||
defp check_file_size(path, size_limit) when is_integer(size_limit) and size_limit > 0 do
|
||||
with {:ok, %{size: size}} <- File.stat(path),
|
||||
true <- size <= size_limit do
|
||||
:ok
|
||||
else
|
||||
false -> {:error, :file_too_large}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp check_file_size(_, _), do: :ok
|
||||
|
||||
# Creates a tempfile using the Plug.Upload Genserver which cleans them up
|
||||
# automatically.
|
||||
defp tempfile_for_image(data) do
|
||||
{:ok, tmp_path} = Plug.Upload.random_file("profile_pics")
|
||||
{:ok, tmp_file} = File.open(tmp_path, [:write, :raw, :binary])
|
||||
IO.binwrite(tmp_file, data)
|
||||
@ -83,108 +211,12 @@ defmodule Pleroma.Upload do
|
||||
tmp_path
|
||||
end
|
||||
|
||||
def strip_exif_data(content_type, file) do
|
||||
settings = Application.get_env(:pleroma, Pleroma.Upload)
|
||||
do_strip = Keyword.fetch!(settings, :strip_exif)
|
||||
[filetype, _ext] = String.split(content_type, "/")
|
||||
|
||||
if filetype == "image" and do_strip == true do
|
||||
Mogrify.open(file) |> Mogrify.custom("strip") |> Mogrify.save(in_place: true)
|
||||
end
|
||||
defp url_from_spec(base_url, {:file, path}) do
|
||||
[base_url, "media", path]
|
||||
|> Path.join()
|
||||
end
|
||||
|
||||
defp create_name(uuid, ext, type) do
|
||||
case type do
|
||||
"application/octet-stream" ->
|
||||
String.downcase(Enum.join([uuid, ext], "."))
|
||||
|
||||
"audio/mpeg" ->
|
||||
String.downcase(Enum.join([uuid, "mp3"], "."))
|
||||
|
||||
_ ->
|
||||
String.downcase(Enum.join([uuid, List.last(String.split(type, "/"))], "."))
|
||||
end
|
||||
end
|
||||
|
||||
defp get_uuid(file, should_dedupe) do
|
||||
if should_dedupe do
|
||||
Base.encode16(:crypto.hash(:sha256, File.read!(file.path)))
|
||||
else
|
||||
UUID.generate()
|
||||
end
|
||||
end
|
||||
|
||||
defp get_name(file, uuid, type, should_dedupe) do
|
||||
if should_dedupe do
|
||||
create_name(uuid, List.last(String.split(file.filename, ".")), type)
|
||||
else
|
||||
parts = String.split(file.filename, ".")
|
||||
|
||||
new_filename =
|
||||
if length(parts) > 1 do
|
||||
Enum.drop(parts, -1) |> Enum.join(".")
|
||||
else
|
||||
Enum.join(parts)
|
||||
end
|
||||
|
||||
case type do
|
||||
"application/octet-stream" -> file.filename
|
||||
"audio/mpeg" -> new_filename <> ".mp3"
|
||||
"image/jpeg" -> new_filename <> ".jpg"
|
||||
_ -> Enum.join([new_filename, String.split(type, "/") |> List.last()], ".")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_content_type(file) do
|
||||
match =
|
||||
File.open(file, [:read], fn f ->
|
||||
case IO.binread(f, 8) do
|
||||
<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A>> ->
|
||||
"image/png"
|
||||
|
||||
<<0x47, 0x49, 0x46, 0x38, _, 0x61, _, _>> ->
|
||||
"image/gif"
|
||||
|
||||
<<0xFF, 0xD8, 0xFF, _, _, _, _, _>> ->
|
||||
"image/jpeg"
|
||||
|
||||
<<0x1A, 0x45, 0xDF, 0xA3, _, _, _, _>> ->
|
||||
"video/webm"
|
||||
|
||||
<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70>> ->
|
||||
"video/mp4"
|
||||
|
||||
<<0x49, 0x44, 0x33, _, _, _, _, _>> ->
|
||||
"audio/mpeg"
|
||||
|
||||
<<255, 251, _, 68, 0, 0, 0, 0>> ->
|
||||
"audio/mpeg"
|
||||
|
||||
<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> ->
|
||||
case IO.binread(f, 27) do
|
||||
<<_::size(160), 0x80, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61>> ->
|
||||
"video/ogg"
|
||||
|
||||
_ ->
|
||||
"audio/ogg"
|
||||
end
|
||||
|
||||
<<0x52, 0x49, 0x46, 0x46, _, _, _, _>> ->
|
||||
"audio/wav"
|
||||
|
||||
_ ->
|
||||
"application/octet-stream"
|
||||
end
|
||||
end)
|
||||
|
||||
case match do
|
||||
{:ok, type} -> type
|
||||
_e -> "application/octet-stream"
|
||||
end
|
||||
end
|
||||
|
||||
defp uploader() do
|
||||
Pleroma.Config.get!([Pleroma.Upload, :uploader])
|
||||
defp url_from_spec({:url, url}) do
|
||||
url
|
||||
end
|
||||
end
|
||||
|
35
lib/pleroma/upload/filter.ex
Normal file
35
lib/pleroma/upload/filter.ex
Normal file
@ -0,0 +1,35 @@
|
||||
defmodule Pleroma.Upload.Filter do
|
||||
@moduledoc """
|
||||
Upload Filter behaviour
|
||||
|
||||
This behaviour allows to run filtering actions just before a file is uploaded. This allows to:
|
||||
|
||||
* morph in place the temporary file
|
||||
* change any field of a `Pleroma.Upload` struct
|
||||
* cancel/stop the upload
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
@callback filter(Pleroma.Upload.t()) :: :ok | {:ok, Pleroma.Upload.t()} | {:error, any()}
|
||||
|
||||
@spec filter([module()], Pleroma.Upload.t()) :: {:ok, Pleroma.Upload.t()} | {:error, any()}
|
||||
|
||||
def filter([], upload) do
|
||||
{:ok, upload}
|
||||
end
|
||||
|
||||
def filter([filter | rest], upload) do
|
||||
case filter.filter(upload) do
|
||||
:ok ->
|
||||
filter(rest, upload)
|
||||
|
||||
{:ok, upload} ->
|
||||
filter(rest, upload)
|
||||
|
||||
error ->
|
||||
Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(error)}")
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
10
lib/pleroma/upload/filter/anonymize_filename.ex
Normal file
10
lib/pleroma/upload/filter/anonymize_filename.ex
Normal file
@ -0,0 +1,10 @@
|
||||
defmodule Pleroma.Upload.Filter.AnonymizeFilename do
|
||||
@moduledoc "Replaces the original filename with a randomly generated string."
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
def filter(upload) do
|
||||
extension = List.last(String.split(upload.name, "."))
|
||||
string = Base.url_encode64(:crypto.strong_rand_bytes(10), padding: false)
|
||||
{:ok, %Pleroma.Upload{upload | name: string <> "." <> extension}}
|
||||
end
|
||||
end
|
10
lib/pleroma/upload/filter/dedupe.ex
Normal file
10
lib/pleroma/upload/filter/dedupe.ex
Normal file
@ -0,0 +1,10 @@
|
||||
defmodule Pleroma.Upload.Filter.Dedupe do
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
def filter(upload = %Pleroma.Upload{name: name, tempfile: path}) do
|
||||
extension = String.split(name, ".") |> List.last()
|
||||
shasum = :crypto.hash(:sha256, File.read!(upload.tempfile)) |> Base.encode16(case: :lower)
|
||||
filename = shasum <> "." <> extension
|
||||
{:ok, %Pleroma.Upload{upload | id: shasum, path: filename}}
|
||||
end
|
||||
end
|
60
lib/pleroma/upload/filter/mogrifun.ex
Normal file
60
lib/pleroma/upload/filter/mogrifun.ex
Normal file
@ -0,0 +1,60 @@
|
||||
defmodule Pleroma.Upload.Filter.Mogrifun do
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@filters [
|
||||
{"implode", "1"},
|
||||
{"-raise", "20"},
|
||||
{"+raise", "20"},
|
||||
[{"-interpolate", "nearest"}, {"-virtual-pixel", "mirror"}, {"-spread", "5"}],
|
||||
"+polaroid",
|
||||
{"-statistic", "Mode 10"},
|
||||
{"-emboss", "0x1.1"},
|
||||
{"-emboss", "0x2"},
|
||||
{"-colorspace", "Gray"},
|
||||
"-negate",
|
||||
[{"-channel", "green"}, "-negate"],
|
||||
[{"-channel", "red"}, "-negate"],
|
||||
[{"-channel", "blue"}, "-negate"],
|
||||
{"+level-colors", "green,gold"},
|
||||
{"+level-colors", ",DodgerBlue"},
|
||||
{"+level-colors", ",Gold"},
|
||||
{"+level-colors", ",Lime"},
|
||||
{"+level-colors", ",Red"},
|
||||
{"+level-colors", ",DarkGreen"},
|
||||
{"+level-colors", "firebrick,yellow"},
|
||||
{"+level-colors", "'rgb(102,75,25)',lemonchiffon"},
|
||||
[{"fill", "red"}, {"tint", "40"}],
|
||||
[{"fill", "green"}, {"tint", "40"}],
|
||||
[{"fill", "blue"}, {"tint", "40"}],
|
||||
[{"fill", "yellow"}, {"tint", "40"}]
|
||||
]
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
|
||||
filter = Enum.random(@filters)
|
||||
|
||||
file
|
||||
|> Mogrify.open()
|
||||
|> mogrify_filter(filter)
|
||||
|> Mogrify.save(in_place: true)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def filter(_), do: :ok
|
||||
|
||||
defp mogrify_filter(mogrify, [filter | rest]) do
|
||||
mogrify
|
||||
|> mogrify_filter(filter)
|
||||
|> mogrify_filter(rest)
|
||||
end
|
||||
|
||||
defp mogrify_filter(mogrify, []), do: mogrify
|
||||
|
||||
defp mogrify_filter(mogrify, {action, options}) do
|
||||
Mogrify.custom(mogrify, action, options)
|
||||
end
|
||||
|
||||
defp mogrify_filter(mogrify, string) when is_binary(string) do
|
||||
Mogrify.custom(mogrify, string)
|
||||
end
|
||||
end
|
37
lib/pleroma/upload/filter/mogrify.ex
Normal file
37
lib/pleroma/upload/filter/mogrify.ex
Normal file
@ -0,0 +1,37 @@
|
||||
defmodule Pleroma.Upload.Filter.Mogrify do
|
||||
@behaviour Pleroma.Uploader.Filter
|
||||
|
||||
@type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()}
|
||||
@type conversions :: conversion() | [conversion()]
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
|
||||
filters = Pleroma.Config.get!([__MODULE__, :args])
|
||||
|
||||
file
|
||||
|> Mogrify.open()
|
||||
|> mogrify_filter(filters)
|
||||
|> Mogrify.save(in_place: true)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def filter(_), do: :ok
|
||||
|
||||
defp mogrify_filter(mogrify, nil), do: mogrify
|
||||
|
||||
defp mogrify_filter(mogrify, [filter | rest]) do
|
||||
mogrify
|
||||
|> mogrify_filter(filter)
|
||||
|> mogrify_filter(rest)
|
||||
end
|
||||
|
||||
defp mogrify_filter(mogrify, []), do: mogrify
|
||||
|
||||
defp mogrify_filter(mogrify, {action, options}) do
|
||||
Mogrify.custom(mogrify, action, options)
|
||||
end
|
||||
|
||||
defp mogrify_filter(mogrify, action) when is_binary(action) do
|
||||
Mogrify.custom(mogrify, action)
|
||||
end
|
||||
end
|
@ -3,49 +3,32 @@ defmodule Pleroma.Uploaders.Local do
|
||||
|
||||
alias Pleroma.Web
|
||||
|
||||
def put_file(name, uuid, tmpfile, _content_type, should_dedupe) do
|
||||
upload_folder = get_upload_path(uuid, should_dedupe)
|
||||
url_path = get_url(name, uuid, should_dedupe)
|
||||
def get_file(_) do
|
||||
{:ok, {:static_dir, upload_path()}}
|
||||
end
|
||||
|
||||
File.mkdir_p!(upload_folder)
|
||||
def put_file(upload) do
|
||||
{local_path, file} =
|
||||
case Enum.reverse(String.split(upload.path, "/", trim: true)) do
|
||||
[file] ->
|
||||
{upload_path(), file}
|
||||
|
||||
result_file = Path.join(upload_folder, name)
|
||||
[file | folders] ->
|
||||
path = Path.join([upload_path()] ++ Enum.reverse(folders))
|
||||
File.mkdir_p!(path)
|
||||
{path, file}
|
||||
end
|
||||
|
||||
if File.exists?(result_file) do
|
||||
File.rm!(tmpfile)
|
||||
else
|
||||
File.cp!(tmpfile, result_file)
|
||||
result_file = Path.join(local_path, file)
|
||||
|
||||
unless File.exists?(result_file) do
|
||||
File.cp!(upload.tempfile, result_file)
|
||||
end
|
||||
|
||||
{:ok, url_path}
|
||||
:ok
|
||||
end
|
||||
|
||||
def upload_path do
|
||||
settings = Application.get_env(:pleroma, Pleroma.Uploaders.Local)
|
||||
Keyword.fetch!(settings, :uploads)
|
||||
end
|
||||
|
||||
defp get_upload_path(uuid, should_dedupe) do
|
||||
if should_dedupe do
|
||||
upload_path()
|
||||
else
|
||||
Path.join(upload_path(), uuid)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_url(name, uuid, should_dedupe) do
|
||||
if should_dedupe do
|
||||
url_for(:cow_uri.urlencode(name))
|
||||
else
|
||||
url_for(Path.join(uuid, :cow_uri.urlencode(name)))
|
||||
end
|
||||
end
|
||||
|
||||
defp url_for(file) do
|
||||
settings = Application.get_env(:pleroma, Pleroma.Uploaders.Local)
|
||||
|
||||
Keyword.get(settings, :uploads_url)
|
||||
|> String.replace("{{file}}", file)
|
||||
|> String.replace("{{base_url}}", Web.base_url())
|
||||
Pleroma.Config.get!([__MODULE__, :uploads])
|
||||
end
|
||||
end
|
||||
|
@ -5,22 +5,27 @@ defmodule Pleroma.Uploaders.MDII do
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
def put_file(name, uuid, path, content_type, should_dedupe) do
|
||||
# MDII-hosted images are never passed through the MediaPlug; only local media.
|
||||
# Delegate to Pleroma.Uploaders.Local
|
||||
def get_file(file) do
|
||||
Pleroma.Uploaders.Local.get_file(file)
|
||||
end
|
||||
|
||||
def put_file(upload) do
|
||||
cgi = Pleroma.Config.get([Pleroma.Uploaders.MDII, :cgi])
|
||||
files = Pleroma.Config.get([Pleroma.Uploaders.MDII, :files])
|
||||
|
||||
{:ok, file_data} = File.read(path)
|
||||
{:ok, file_data} = File.read(upload.tempfile)
|
||||
|
||||
extension = String.split(name, ".") |> List.last()
|
||||
extension = String.split(upload.name, ".") |> List.last()
|
||||
query = "#{cgi}?#{extension}"
|
||||
|
||||
with {:ok, %{status_code: 200, body: body}} <- @httpoison.post(query, file_data) do
|
||||
File.rm!(path)
|
||||
with {:ok, %{status: 200, body: body}} <- @httpoison.post(query, file_data) do
|
||||
remote_file_name = String.split(body) |> List.first()
|
||||
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
||||
{:ok, public_url}
|
||||
{:ok, {:url, public_url}}
|
||||
else
|
||||
_ -> Pleroma.Uploaders.Local.put_file(name, uuid, path, content_type, should_dedupe)
|
||||
_ -> Pleroma.Uploaders.Local.put_file(upload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,40 +1,46 @@
|
||||
defmodule Pleroma.Uploaders.S3 do
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
@behaviour Pleroma.Uploaders.Uploader
|
||||
require Logger
|
||||
|
||||
def put_file(name, uuid, path, content_type, _should_dedupe) do
|
||||
settings = Application.get_env(:pleroma, Pleroma.Uploaders.S3)
|
||||
bucket = Keyword.fetch!(settings, :bucket)
|
||||
public_endpoint = Keyword.fetch!(settings, :public_endpoint)
|
||||
force_media_proxy = Keyword.fetch!(settings, :force_media_proxy)
|
||||
# The file name is re-encoded with S3's constraints here to comply with previous links with less strict filenames
|
||||
def get_file(file) do
|
||||
config = Pleroma.Config.get([__MODULE__])
|
||||
|
||||
{:ok, file_data} = File.read(path)
|
||||
{:ok,
|
||||
{:url,
|
||||
Path.join([
|
||||
Keyword.fetch!(config, :public_endpoint),
|
||||
Keyword.fetch!(config, :bucket),
|
||||
strict_encode(URI.decode(file))
|
||||
])}}
|
||||
end
|
||||
|
||||
File.rm!(path)
|
||||
def put_file(upload = %Pleroma.Upload{}) do
|
||||
config = Pleroma.Config.get([__MODULE__])
|
||||
bucket = Keyword.get(config, :bucket)
|
||||
|
||||
s3_name = "#{uuid}/#{encode(name)}"
|
||||
{:ok, file_data} = File.read(upload.tempfile)
|
||||
|
||||
{:ok, _} =
|
||||
s3_name = strict_encode(upload.path)
|
||||
|
||||
op =
|
||||
ExAws.S3.put_object(bucket, s3_name, file_data, [
|
||||
{:acl, :public_read},
|
||||
{:content_type, content_type}
|
||||
{:content_type, upload.content_type}
|
||||
])
|
||||
|> ExAws.request()
|
||||
|
||||
url_base = "#{public_endpoint}/#{bucket}/#{s3_name}"
|
||||
case ExAws.request(op) do
|
||||
{:ok, _} ->
|
||||
{:ok, {:file, s3_name}}
|
||||
|
||||
public_url =
|
||||
if force_media_proxy do
|
||||
MediaProxy.url(url_base)
|
||||
else
|
||||
url_base
|
||||
end
|
||||
|
||||
{:ok, public_url}
|
||||
error ->
|
||||
Logger.error("#{__MODULE__}: #{inspect(error)}")
|
||||
{:error, "S3 Upload failed"}
|
||||
end
|
||||
end
|
||||
|
||||
defp encode(name) do
|
||||
String.replace(name, ~r/[^0-9a-zA-Z!.*'()_-]/, "-")
|
||||
@regex Regex.compile!("[^0-9a-zA-Z!.*/'()_-]")
|
||||
def strict_encode(name) do
|
||||
String.replace(name, @regex, "-")
|
||||
end
|
||||
end
|
||||
|
@ -25,10 +25,10 @@ defmodule Pleroma.Uploaders.Swift.Keystone do
|
||||
["Content-Type": "application/json"],
|
||||
hackney: [:insecure]
|
||||
) do
|
||||
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: body}} ->
|
||||
body["access"]["token"]["id"]
|
||||
|
||||
{:ok, %HTTPoison.Response{status_code: _}} ->
|
||||
{:ok, %Tesla.Env{status: _}} ->
|
||||
""
|
||||
end
|
||||
end
|
||||
|
@ -13,10 +13,10 @@ defmodule Pleroma.Uploaders.Swift.Client do
|
||||
token = Pleroma.Uploaders.Swift.Keystone.get_token()
|
||||
|
||||
case put("#{filename}", body, "X-Auth-Token": token, "Content-Type": content_type) do
|
||||
{:ok, %HTTPoison.Response{status_code: 201}} ->
|
||||
{:ok, "#{object_url}/#{filename}"}
|
||||
{:ok, %Tesla.Env{status: 201}} ->
|
||||
{:ok, {:file, filename}}
|
||||
|
||||
{:ok, %HTTPoison.Response{status_code: 401}} ->
|
||||
{:ok, %Tesla.Env{status: 401}} ->
|
||||
{:error, "Unauthorized, Bad Token"}
|
||||
|
||||
{:error, _} ->
|
||||
|
@ -1,10 +1,15 @@
|
||||
defmodule Pleroma.Uploaders.Swift do
|
||||
@behaviour Pleroma.Uploaders.Uploader
|
||||
|
||||
def put_file(name, uuid, tmp_path, content_type, _should_dedupe) do
|
||||
{:ok, file_data} = File.read(tmp_path)
|
||||
remote_name = "#{uuid}/#{name}"
|
||||
def get_file(name) do
|
||||
{:ok, {:url, Path.join([Pleroma.Config.get!([__MODULE__, :object_url]), name])}}
|
||||
end
|
||||
|
||||
Pleroma.Uploaders.Swift.Client.upload_file(remote_name, file_data, content_type)
|
||||
def put_file(upload) do
|
||||
Pleroma.Uploaders.Swift.Client.upload_file(
|
||||
upload.path,
|
||||
File.read!(upload.tmpfile),
|
||||
upload.content_type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -1,20 +1,40 @@
|
||||
defmodule Pleroma.Uploaders.Uploader do
|
||||
@moduledoc """
|
||||
Defines the contract to put an uploaded file to any backend.
|
||||
Defines the contract to put and get an uploaded file to any backend.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Instructs how to get the file from the backend.
|
||||
|
||||
Used by `Pleroma.Plugs.UploadedMedia`.
|
||||
"""
|
||||
@type get_method :: {:static_dir, directory :: String.t()} | {:url, url :: String.t()}
|
||||
@callback get_file(file :: String.t()) :: {:ok, get_method()}
|
||||
|
||||
@doc """
|
||||
Put a file to the backend.
|
||||
|
||||
Returns `{:ok, String.t } | {:error, String.t} containing the path of the
|
||||
uploaded file, or error information if the file failed to be saved to the
|
||||
respective backend.
|
||||
Returns:
|
||||
|
||||
* `:ok` which assumes `{:ok, upload.path}`
|
||||
* `{:ok, spec}` where spec is:
|
||||
* `{:file, filename :: String.t}` to handle reads with `get_file/1` (recommended)
|
||||
|
||||
This allows to correctly proxy or redirect requests to the backend, while allowing to migrate backends without breaking any URL.
|
||||
* `{url, url :: String.t}` to bypass `get_file/2` and use the `url` directly in the activity.
|
||||
* `{:error, String.t}` error information if the file failed to be saved to the backend.
|
||||
|
||||
|
||||
"""
|
||||
@callback put_file(
|
||||
name :: String.t(),
|
||||
uuid :: String.t(),
|
||||
file :: File.t(),
|
||||
content_type :: String.t(),
|
||||
should_dedupe :: Boolean.t()
|
||||
) :: {:ok, String.t()} | {:error, String.t()}
|
||||
@callback put_file(Pleroma.Upload.t()) ::
|
||||
:ok | {:ok, {:file | :url, String.t()}} | {:error, String.t()}
|
||||
|
||||
@spec put_file(module(), Pleroma.Upload.t()) ::
|
||||
{:ok, {:file | :url, String.t()}} | {:error, String.t()}
|
||||
def put_file(uploader, upload) do
|
||||
case uploader.put_file(upload) do
|
||||
:ok -> {:ok, {:file, upload.path}}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,8 @@ defmodule Pleroma.User do
|
||||
import Ecto.{Changeset, Query}
|
||||
alias Pleroma.{Repo, User, Object, Web, Activity, Notification}
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||
alias Pleroma.Web.{OStatus, Websub, OAuth}
|
||||
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
|
||||
|
||||
@ -19,11 +21,11 @@ defmodule Pleroma.User do
|
||||
field(:ap_id, :string)
|
||||
field(:avatar, :map)
|
||||
field(:local, :boolean, default: true)
|
||||
field(:info, :map, default: %{})
|
||||
field(:follower_address, :string)
|
||||
field(:search_distance, :float, virtual: true)
|
||||
field(:last_refreshed_at, :naive_datetime)
|
||||
has_many(:notifications, Notification)
|
||||
embeds_one(:info, Pleroma.User.Info)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@ -36,13 +38,13 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def banner_url(user) do
|
||||
case user.info["banner"] do
|
||||
case user.info.banner do
|
||||
%{"url" => [%{"href" => href} | _]} -> href
|
||||
_ -> "#{Web.base_url()}/images/banner.png"
|
||||
end
|
||||
end
|
||||
|
||||
def profile_url(%User{info: %{"source_data" => %{"url" => url}}}), do: url
|
||||
def profile_url(%User{info: %{source_data: %{"url" => url}}}), do: url
|
||||
def profile_url(%User{ap_id: ap_id}), do: ap_id
|
||||
def profile_url(_), do: nil
|
||||
|
||||
@ -61,9 +63,7 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def info_changeset(struct, params \\ %{}) do
|
||||
struct
|
||||
|> cast(params, [:info])
|
||||
|> validate_required([:info])
|
||||
raise "NOT VALID ANYMORE"
|
||||
end
|
||||
|
||||
def user_info(%User{} = user) do
|
||||
@ -71,27 +71,34 @@ defmodule Pleroma.User do
|
||||
|
||||
%{
|
||||
following_count: length(user.following) - oneself,
|
||||
note_count: user.info["note_count"] || 0,
|
||||
follower_count: user.info["follower_count"] || 0,
|
||||
locked: user.info["locked"] || false,
|
||||
default_scope: user.info["default_scope"] || "public"
|
||||
note_count: user.info.note_count,
|
||||
follower_count: user.info.follower_count,
|
||||
locked: user.info.locked,
|
||||
default_scope: user.info.default_scope
|
||||
}
|
||||
end
|
||||
|
||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||
def remote_user_creation(params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put(:info, params[:info] || %{})
|
||||
|
||||
info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info])
|
||||
|
||||
changes =
|
||||
%User{}
|
||||
|> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar])
|
||||
|> cast(params, [:bio, :name, :ap_id, :nickname, :avatar])
|
||||
|> validate_required([:name, :ap_id])
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, @email_regex)
|
||||
|> validate_length(:bio, max: 5000)
|
||||
|> validate_length(:name, max: 100)
|
||||
|> put_change(:local, false)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
if changes.valid? do
|
||||
case changes.changes[:info]["source_data"] do
|
||||
case info_cng.changes[:source_data] do
|
||||
%{"followers" => followers} ->
|
||||
changes
|
||||
|> put_change(:follower_address, followers)
|
||||
@ -109,7 +116,7 @@ defmodule Pleroma.User do
|
||||
|
||||
def update_changeset(struct, params \\ %{}) do
|
||||
struct
|
||||
|> cast(params, [:bio, :name])
|
||||
|> cast(params, [:bio, :name, :avatar])
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/)
|
||||
|> validate_length(:bio, max: 5000)
|
||||
@ -121,12 +128,17 @@ defmodule Pleroma.User do
|
||||
params
|
||||
|> Map.put(:last_refreshed_at, NaiveDateTime.utc_now())
|
||||
|
||||
info_cng =
|
||||
struct.info
|
||||
|> User.Info.user_upgrade(params[:info])
|
||||
|
||||
struct
|
||||
|> cast(params, [:bio, :name, :info, :follower_address, :avatar, :last_refreshed_at])
|
||||
|> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at])
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/)
|
||||
|> validate_length(:bio, max: 5000)
|
||||
|> validate_length(:name, max: 100)
|
||||
|> put_embed(:info, info_cng)
|
||||
end
|
||||
|
||||
def password_update_changeset(struct, params) do
|
||||
@ -165,6 +177,7 @@ defmodule Pleroma.User do
|
||||
|> validate_format(:email, @email_regex)
|
||||
|> validate_length(:bio, max: 1000)
|
||||
|> validate_length(:name, min: 1, max: 100)
|
||||
|> put_change(:info, %Pleroma.User.Info{})
|
||||
|
||||
if changeset.valid? do
|
||||
hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
|
||||
@ -191,7 +204,7 @@ defmodule Pleroma.User do
|
||||
|
||||
def needs_update?(_), do: true
|
||||
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{"locked" => true}}) do
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
|
||||
{:ok, follower}
|
||||
end
|
||||
|
||||
@ -222,7 +235,7 @@ defmodule Pleroma.User do
|
||||
ap_followers = followed.follower_address
|
||||
|
||||
cond do
|
||||
following?(follower, followed) or info["deactivated"] ->
|
||||
following?(follower, followed) or info.deactivated ->
|
||||
{:error, "Could not follow user: #{followed.nickname} is already on your list."}
|
||||
|
||||
deny_follow_blocked and blocks?(followed, follower) ->
|
||||
@ -274,7 +287,7 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def locked?(%User{} = user) do
|
||||
user.info["locked"] || false
|
||||
user.info.locked || false
|
||||
end
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
@ -411,22 +424,23 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def increase_note_count(%User{} = user) do
|
||||
note_count = (user.info["note_count"] || 0) + 1
|
||||
new_info = Map.put(user.info, "note_count", note_count)
|
||||
info_cng = User.Info.add_to_note_count(user.info, 1)
|
||||
|
||||
cs = info_changeset(user, %{info: new_info})
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cs)
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def decrease_note_count(%User{} = user) do
|
||||
note_count = user.info["note_count"] || 0
|
||||
note_count = if note_count <= 0, do: 0, else: note_count - 1
|
||||
new_info = Map.put(user.info, "note_count", note_count)
|
||||
info_cng = User.Info.add_to_note_count(user.info, -1)
|
||||
|
||||
cs = info_changeset(user, %{info: new_info})
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cs)
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def update_note_count(%User{} = user) do
|
||||
@ -439,11 +453,13 @@ defmodule Pleroma.User do
|
||||
|
||||
note_count = Repo.one(note_count_query)
|
||||
|
||||
new_info = Map.put(user.info, "note_count", note_count)
|
||||
info_cng = User.Info.set_note_count(user.info, note_count)
|
||||
|
||||
cs = info_changeset(user, %{info: new_info})
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cs)
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def update_follower_count(%User{} = user) do
|
||||
@ -457,11 +473,15 @@ defmodule Pleroma.User do
|
||||
|
||||
follower_count = Repo.one(follower_count_query)
|
||||
|
||||
new_info = Map.put(user.info, "follower_count", follower_count)
|
||||
info_cng =
|
||||
user.info
|
||||
|> User.Info.set_follower_count(follower_count)
|
||||
|
||||
cs = info_changeset(user, %{info: new_info})
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cs)
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def get_users_from_set_query(ap_ids, false) do
|
||||
@ -545,12 +565,15 @@ defmodule Pleroma.User do
|
||||
unfollow(blocked, blocker)
|
||||
end
|
||||
|
||||
blocks = blocker.info["blocks"] || []
|
||||
new_blocks = Enum.uniq([ap_id | blocks])
|
||||
new_info = Map.put(blocker.info, "blocks", new_blocks)
|
||||
info_cng =
|
||||
blocker.info
|
||||
|> User.Info.add_to_block(ap_id)
|
||||
|
||||
cs = User.info_changeset(blocker, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
cng =
|
||||
change(blocker)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
# helper to handle the block given only an actor's AP id
|
||||
@ -558,18 +581,21 @@ defmodule Pleroma.User do
|
||||
block(blocker, User.get_by_ap_id(ap_id))
|
||||
end
|
||||
|
||||
def unblock(user, %{ap_id: ap_id}) do
|
||||
blocks = user.info["blocks"] || []
|
||||
new_blocks = List.delete(blocks, ap_id)
|
||||
new_info = Map.put(user.info, "blocks", new_blocks)
|
||||
def unblock(blocker, %{ap_id: ap_id}) do
|
||||
info_cng =
|
||||
blocker.info
|
||||
|> User.Info.remove_from_block(ap_id)
|
||||
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
cng =
|
||||
change(blocker)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def blocks?(user, %{ap_id: ap_id}) do
|
||||
blocks = user.info["blocks"] || []
|
||||
domain_blocks = user.info["domain_blocks"] || []
|
||||
blocks = user.info.blocks
|
||||
domain_blocks = user.info.domain_blocks
|
||||
%{host: host} = URI.parse(ap_id)
|
||||
|
||||
Enum.member?(blocks, ap_id) ||
|
||||
@ -579,21 +605,27 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def block_domain(user, domain) do
|
||||
domain_blocks = user.info["domain_blocks"] || []
|
||||
new_blocks = Enum.uniq([domain | domain_blocks])
|
||||
new_info = Map.put(user.info, "domain_blocks", new_blocks)
|
||||
info_cng =
|
||||
user.info
|
||||
|> User.Info.add_to_domain_block(domain)
|
||||
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def unblock_domain(user, domain) do
|
||||
blocks = user.info["domain_blocks"] || []
|
||||
new_blocks = List.delete(blocks, domain)
|
||||
new_info = Map.put(user.info, "domain_blocks", new_blocks)
|
||||
info_cng =
|
||||
user.info
|
||||
|> User.Info.remove_from_domain_block(domain)
|
||||
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def local_user_query() do
|
||||
@ -613,9 +645,13 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def deactivate(%User{} = user, status \\ true) do
|
||||
new_info = Map.put(user.info, "deactivated", status)
|
||||
cs = User.info_changeset(user, %{info: new_info})
|
||||
update_and_set_cache(cs)
|
||||
info_cng = User.Info.set_activation_status(user.info, status)
|
||||
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def delete(%User{} = user) do
|
||||
@ -649,7 +685,7 @@ defmodule Pleroma.User do
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
def html_filter_policy(%User{info: %{"no_rich_text" => true}}) do
|
||||
def html_filter_policy(%User{info: %{no_rich_text: true}}) do
|
||||
Pleroma.HTML.Scrubber.TwitterText
|
||||
end
|
||||
|
||||
@ -683,7 +719,7 @@ defmodule Pleroma.User do
|
||||
user
|
||||
else
|
||||
changes =
|
||||
%User{}
|
||||
%User{info: %User.Info{}}
|
||||
|> cast(%{}, [:ap_id, :nickname, :local])
|
||||
|> put_change(:ap_id, relay_uri)
|
||||
|> put_change(:nickname, nil)
|
||||
@ -697,7 +733,7 @@ defmodule Pleroma.User do
|
||||
|
||||
# AP style
|
||||
def public_key_from_info(%{
|
||||
"source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}}
|
||||
source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}
|
||||
}) do
|
||||
key =
|
||||
:public_key.pem_decode(public_key_pem)
|
||||
@ -708,7 +744,7 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
# OStatus Magic Key
|
||||
def public_key_from_info(%{"magic_key" => magic_key}) do
|
||||
def public_key_from_info(%{magic_key: magic_key}) do
|
||||
{:ok, Pleroma.Web.Salmon.decode_key(magic_key)}
|
||||
end
|
||||
|
||||
@ -730,11 +766,12 @@ defmodule Pleroma.User do
|
||||
|> Map.put(:name, blank?(data[:name]) || data[:nickname])
|
||||
|
||||
cs = User.remote_user_creation(data)
|
||||
|
||||
Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
|
||||
end
|
||||
|
||||
def ap_enabled?(%User{local: true}), do: true
|
||||
def ap_enabled?(%User{info: info}), do: info["ap_enabled"]
|
||||
def ap_enabled?(%User{info: info}), do: info.ap_enabled
|
||||
def ap_enabled?(_), do: false
|
||||
|
||||
def get_or_fetch(uri_or_nickname) do
|
||||
@ -768,4 +805,18 @@ defmodule Pleroma.User do
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do
|
||||
mentions = Formatter.parse_mentions(bio)
|
||||
tags = Formatter.parse_tags(bio)
|
||||
|
||||
emoji =
|
||||
(user.info.source_data["tag"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||
{String.trim(name, ":"), url}
|
||||
end)
|
||||
|
||||
CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji)
|
||||
end
|
||||
end
|
||||
|
167
lib/pleroma/user/info.ex
Normal file
167
lib/pleroma/user/info.ex
Normal file
@ -0,0 +1,167 @@
|
||||
defmodule Pleroma.User.Info do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
embedded_schema do
|
||||
field(:banner, :map, default: %{})
|
||||
field(:background, :map, default: %{})
|
||||
field(:source_data, :map, default: %{})
|
||||
field(:note_count, :integer, default: 0)
|
||||
field(:follower_count, :integer, default: 0)
|
||||
field(:locked, :boolean, default: false)
|
||||
field(:default_scope, :string, default: "public")
|
||||
field(:blocks, {:array, :string}, default: [])
|
||||
field(:domain_blocks, {:array, :string}, default: [])
|
||||
field(:deactivated, :boolean, default: false)
|
||||
field(:no_rich_text, :boolean, default: false)
|
||||
field(:ap_enabled, :boolean, default: false)
|
||||
field(:is_moderator, :boolean, default: false)
|
||||
field(:is_admin, :boolean, default: false)
|
||||
field(:keys, :string, default: nil)
|
||||
field(:settings, :map, default: nil)
|
||||
field(:magic_key, :string, default: nil)
|
||||
field(:uri, :string, default: nil)
|
||||
field(:topic, :string, default: nil)
|
||||
field(:hub, :string, default: nil)
|
||||
field(:salmon, :string, default: nil)
|
||||
field(:hide_network, :boolean, default: false)
|
||||
|
||||
# Found in the wild
|
||||
# ap_id -> Where is this used?
|
||||
# bio -> Where is this used?
|
||||
# avatar -> Where is this used?
|
||||
# fqn -> Where is this used?
|
||||
# host -> Where is this used?
|
||||
# subject _> Where is this used?
|
||||
end
|
||||
|
||||
def set_activation_status(info, deactivated) do
|
||||
params = %{deactivated: deactivated}
|
||||
|
||||
info
|
||||
|> cast(params, [:deactivated])
|
||||
|> validate_required([:deactivated])
|
||||
end
|
||||
|
||||
def add_to_note_count(info, number) do
|
||||
set_note_count(info, info.note_count + number)
|
||||
end
|
||||
|
||||
def set_note_count(info, number) do
|
||||
params = %{note_count: Enum.max([0, number])}
|
||||
|
||||
info
|
||||
|> cast(params, [:note_count])
|
||||
|> validate_required([:note_count])
|
||||
end
|
||||
|
||||
def set_follower_count(info, number) do
|
||||
params = %{follower_count: Enum.max([0, number])}
|
||||
|
||||
info
|
||||
|> cast(params, [:follower_count])
|
||||
|> validate_required([:follower_count])
|
||||
end
|
||||
|
||||
def set_blocks(info, blocks) do
|
||||
params = %{blocks: blocks}
|
||||
|
||||
info
|
||||
|> cast(params, [:blocks])
|
||||
|> validate_required([:blocks])
|
||||
end
|
||||
|
||||
def add_to_block(info, blocked) do
|
||||
set_blocks(info, Enum.uniq([blocked | info.blocks]))
|
||||
end
|
||||
|
||||
def remove_from_block(info, blocked) do
|
||||
set_blocks(info, List.delete(info.blocks, blocked))
|
||||
end
|
||||
|
||||
def set_domain_blocks(info, domain_blocks) do
|
||||
params = %{domain_blocks: domain_blocks}
|
||||
|
||||
info
|
||||
|> cast(params, [:domain_blocks])
|
||||
|> validate_required([:domain_blocks])
|
||||
end
|
||||
|
||||
def add_to_domain_block(info, domain_blocked) do
|
||||
set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks]))
|
||||
end
|
||||
|
||||
def remove_from_domain_block(info, domain_blocked) do
|
||||
set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked))
|
||||
end
|
||||
|
||||
def set_keys(info, keys) do
|
||||
params = %{keys: keys}
|
||||
|
||||
info
|
||||
|> cast(params, [:keys])
|
||||
|> validate_required([:keys])
|
||||
end
|
||||
|
||||
def remote_user_creation(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:ap_enabled,
|
||||
:source_data,
|
||||
:banner,
|
||||
:locked,
|
||||
:magic_key,
|
||||
:uri,
|
||||
:hub,
|
||||
:topic,
|
||||
:salmon
|
||||
])
|
||||
end
|
||||
|
||||
def user_upgrade(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:ap_enabled,
|
||||
:source_data,
|
||||
:banner,
|
||||
:locked,
|
||||
:magic_key
|
||||
])
|
||||
end
|
||||
|
||||
def profile_update(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:locked,
|
||||
:no_rich_text,
|
||||
:default_scope,
|
||||
:banner,
|
||||
:hide_network,
|
||||
:background
|
||||
])
|
||||
end
|
||||
|
||||
def mastodon_profile_update(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:locked,
|
||||
:banner
|
||||
])
|
||||
end
|
||||
|
||||
def set_source_data(info, source_data) do
|
||||
params = %{source_data: source_data}
|
||||
|
||||
info
|
||||
|> cast(params, [:source_data])
|
||||
|> validate_required([:source_data])
|
||||
end
|
||||
|
||||
def admin_api_update(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:is_moderator,
|
||||
:is_admin
|
||||
])
|
||||
end
|
||||
end
|
@ -42,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
defp check_actor_is_active(actor) do
|
||||
if not is_nil(actor) do
|
||||
with user <- User.get_cached_by_ap_id(actor),
|
||||
false <- !!user.info["deactivated"] do
|
||||
false <- user.info.deactivated do
|
||||
:ok
|
||||
else
|
||||
_e -> :reject
|
||||
@ -509,8 +509,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
|
||||
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
|
||||
blocks = info["blocks"] || []
|
||||
domain_blocks = info["domain_blocks"] || []
|
||||
blocks = info.blocks || []
|
||||
domain_blocks = info.domain_blocks || []
|
||||
|
||||
from(
|
||||
activity in query,
|
||||
@ -572,11 +572,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
def upload(file, size_limit \\ nil) do
|
||||
with data <-
|
||||
Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media], size_limit),
|
||||
false <- is_nil(data) do
|
||||
Repo.insert(%Object{data: data})
|
||||
def upload(file, opts \\ []) do
|
||||
with {:ok, data} <- Upload.store(file, opts) do
|
||||
obj_data =
|
||||
if opts[:actor] do
|
||||
Map.put(data, "actor", opts[:actor])
|
||||
else
|
||||
data
|
||||
end
|
||||
|
||||
Repo.insert(%Object{data: obj_data})
|
||||
end
|
||||
end
|
||||
|
||||
@ -678,7 +683,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
remote_inboxes =
|
||||
(Pleroma.Web.Salmon.remote_users(activity) ++ followers)
|
||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||
|> Enum.map(fn %{info: %{"source_data" => data}} ->
|
||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
@ -764,7 +769,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
Logger.info("Fetching #{id} via AP")
|
||||
|
||||
with true <- String.starts_with?(id, "http"),
|
||||
{:ok, %{body: body, status_code: code}} when code in 200..299 <-
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
id,
|
||||
[Accept: "application/activity+json"],
|
||||
|
@ -23,7 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
|
||||
defp check_media_removal(
|
||||
%{host: actor_host} = _actor_info,
|
||||
%{"type" => "Create", "object" => %{"attachement" => child_attachment}} = object
|
||||
%{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
|
||||
)
|
||||
when length(child_attachment) > 0 do
|
||||
object =
|
||||
|
@ -447,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
update_data =
|
||||
new_user_data
|
||||
|> Map.take([:name, :bio, :avatar])
|
||||
|> Map.put(:info, Map.merge(actor.info, %{"banner" => banner, "locked" => locked}))
|
||||
|> Map.put(:info, %{"banner" => banner, "locked" => locked})
|
||||
|
||||
actor
|
||||
|> User.upgrade_changeset(update_data)
|
||||
@ -850,10 +850,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
def upgrade_user_from_ap_id(ap_id, async \\ true) do
|
||||
with %User{local: false} = user <- User.get_by_ap_id(ap_id),
|
||||
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||
data =
|
||||
data
|
||||
|> Map.put(:info, Map.merge(user.info, data[:info]))
|
||||
|
||||
already_ap = User.ap_enabled?(user)
|
||||
|
||||
{:ok, user} =
|
||||
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
# the instance itself is not a Person, but instead an Application
|
||||
def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"])
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
|
||||
def render("user.json", %{user: user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"])
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
@ -55,7 +55,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"name" => user.name,
|
||||
"summary" => user.bio,
|
||||
"url" => user.ap_id,
|
||||
"manuallyApprovesFollowers" => user.info["locked"] || false,
|
||||
"manuallyApprovesFollowers" => user.info.locked,
|
||||
"publicKey" => %{
|
||||
"id" => "#{user.ap_id}#main-key",
|
||||
"owner" => user.ap_id,
|
||||
@ -72,7 +72,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"type" => "Image",
|
||||
"url" => User.banner_url(user)
|
||||
},
|
||||
"tag" => user.info["source_data"]["tag"] || []
|
||||
"tag" => user.info.source_data["tag"] || []
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
query = from(user in query, select: [:ap_id])
|
||||
following = Repo.all(query)
|
||||
|
||||
collection(following, "#{user.ap_id}/following", page)
|
||||
collection(following, "#{user.ap_id}/following", page, !user.info.hide_network)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
@ -95,7 +95,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"id" => "#{user.ap_id}/following",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => length(following),
|
||||
"first" => collection(following, "#{user.ap_id}/following", 1)
|
||||
"first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_network)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
@ -105,7 +105,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
query = from(user in query, select: [:ap_id])
|
||||
followers = Repo.all(query)
|
||||
|
||||
collection(followers, "#{user.ap_id}/followers", page)
|
||||
collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_network)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
@ -118,7 +118,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"id" => "#{user.ap_id}/followers",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => length(followers),
|
||||
"first" => collection(followers, "#{user.ap_id}/followers", 1)
|
||||
"first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_network)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
@ -172,7 +172,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
end
|
||||
end
|
||||
|
||||
def collection(collection, iri, page, total \\ nil) do
|
||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||
offset = (page - 1) * 10
|
||||
items = Enum.slice(collection, offset, 10)
|
||||
items = Enum.map(items, fn user -> user.ap_id end)
|
||||
@ -183,7 +183,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"type" => "OrderedCollectionPage",
|
||||
"partOf" => iri,
|
||||
"totalItems" => total,
|
||||
"orderedItems" => items
|
||||
"orderedItems" => if(show_items, do: items, else: [])
|
||||
}
|
||||
|
||||
if offset < total do
|
||||
|
@ -45,21 +45,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
user = User.get_by_nickname(nickname)
|
||||
|
||||
info =
|
||||
user.info
|
||||
%{}
|
||||
|> Map.put("is_" <> permission_group, true)
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
info_cng = User.Info.admin_api_update(user.info, info)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
{:ok, user} = User.update_and_set_cache(cng)
|
||||
|
||||
conn
|
||||
|> json(user.info)
|
||||
|> json(info)
|
||||
end
|
||||
|
||||
def right_get(conn, %{"nickname" => nickname}) do
|
||||
user = User.get_by_nickname(nickname)
|
||||
|
||||
conn
|
||||
|> json(user.info)
|
||||
|> json(%{
|
||||
is_moderator: user.info.is_moderator,
|
||||
is_admin: user.info.is_admin
|
||||
})
|
||||
end
|
||||
|
||||
def right_add(conn, _) do
|
||||
@ -84,14 +92,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
user = User.get_by_nickname(nickname)
|
||||
|
||||
info =
|
||||
user.info
|
||||
%{}
|
||||
|> Map.put("is_" <> permission_group, false)
|
||||
|
||||
cng = User.info_changeset(user, %{info: info})
|
||||
info_cng = User.Info.admin_api_update(user.info, info)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
{:ok, user} = User.update_and_set_cache(cng)
|
||||
|
||||
conn
|
||||
|> json(user.info)
|
||||
|> json(info)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
def delete(activity_id, user) do
|
||||
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
|
||||
%Object{} = object <- Object.normalize(object_id),
|
||||
true <- user.info["is_moderator"] || user.ap_id == object.data["actor"],
|
||||
true <- user.info.is_moderator || user.ap_id == object.data["actor"],
|
||||
{:ok, delete} <- ActivityPub.delete(object) do
|
||||
{:ok, delete}
|
||||
end
|
||||
@ -135,12 +135,13 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
end
|
||||
end
|
||||
|
||||
# Updates the emojis for a user based on their profile
|
||||
def update(user) do
|
||||
user =
|
||||
with emoji <- emoji_from_profile(user),
|
||||
source_data <- (user.info["source_data"] || %{}) |> Map.put("tag", emoji),
|
||||
new_info <- Map.put(user.info, "source_data", source_data),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji),
|
||||
info_cng <- Pleroma.User.Info.set_source_data(user.info, source_data),
|
||||
change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||
plug(CORSPlug)
|
||||
plug(Pleroma.Plugs.HTTPSecurityPlug)
|
||||
|
||||
plug(Plug.Static, at: "/media", from: Pleroma.Uploaders.Local.upload_path(), gzip: false)
|
||||
plug(Pleroma.Plugs.UploadedMedia)
|
||||
|
||||
plug(
|
||||
Plug.Static,
|
||||
|
@ -17,7 +17,15 @@ defmodule Pleroma.Web.Federator.RetryQueue do
|
||||
end
|
||||
|
||||
def start_link() do
|
||||
GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__)
|
||||
enabled = Pleroma.Config.get([:retry_queue, :enabled], false)
|
||||
|
||||
if enabled do
|
||||
Logger.info("Starting retry queue")
|
||||
GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__)
|
||||
else
|
||||
Logger.info("Retry queue disabled")
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def enqueue(data, transport, retries \\ 0) do
|
||||
|
@ -65,7 +65,7 @@ defmodule Pleroma.Web.HTTPSignatures do
|
||||
end
|
||||
|
||||
def sign(user, headers) do
|
||||
with {:ok, %{info: %{"keys" => keys}}} <- Pleroma.Web.WebFinger.ensure_keys_present(user),
|
||||
with {:ok, %{info: %{keys: keys}}} <- Pleroma.Web.WebFinger.ensure_keys_present(user),
|
||||
{:ok, private_key, _} = Pleroma.Web.Salmon.keys_from_pem(keys) do
|
||||
sigstring = build_signing_string(headers, Map.keys(headers))
|
||||
|
||||
|
@ -32,75 +32,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
end
|
||||
end
|
||||
|
||||
defp add_if_present(
|
||||
map,
|
||||
params,
|
||||
params_field,
|
||||
map_field,
|
||||
value_function \\ fn x -> {:ok, x} end
|
||||
) do
|
||||
if Map.has_key?(params, params_field) do
|
||||
case value_function.(params[params_field]) do
|
||||
{:ok, new_value} -> Map.put(map, map_field, new_value)
|
||||
:error -> map
|
||||
end
|
||||
else
|
||||
map
|
||||
end
|
||||
end
|
||||
|
||||
def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||
original_user = user
|
||||
|
||||
avatar_upload_limit =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.fetch(:avatar_upload_limit)
|
||||
|
||||
banner_upload_limit =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.fetch(:banner_upload_limit)
|
||||
|
||||
params =
|
||||
if bio = params["note"] do
|
||||
Map.put(params, "bio", bio)
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
params =
|
||||
if name = params["display_name"] do
|
||||
Map.put(params, "name", name)
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
user =
|
||||
if avatar = params["avatar"] do
|
||||
with %Plug.Upload{} <- avatar,
|
||||
{:ok, object} <- ActivityPub.upload(avatar, avatar_upload_limit),
|
||||
change = Ecto.Changeset.change(user, %{avatar: object.data}),
|
||||
{:ok, user} = User.update_and_set_cache(change) do
|
||||
user
|
||||
user_params =
|
||||
%{}
|
||||
|> add_if_present(params, "display_name", :name)
|
||||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value)} end)
|
||||
|> add_if_present(params, "avatar", :avatar, fn value ->
|
||||
with %Plug.Upload{} <- value,
|
||||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
|
||||
{:ok, object.data}
|
||||
else
|
||||
_e -> user
|
||||
_ -> :error
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
end)
|
||||
|
||||
user =
|
||||
if banner = params["header"] do
|
||||
with %Plug.Upload{} <- banner,
|
||||
{:ok, object} <- ActivityPub.upload(banner, banner_upload_limit),
|
||||
new_info <- Map.put(user.info, "banner", object.data),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
info_params =
|
||||
%{}
|
||||
|> add_if_present(params, "locked", :locked, fn value -> {:ok, value == "true"} end)
|
||||
|> add_if_present(params, "header", :banner, fn value ->
|
||||
with %Plug.Upload{} <- value,
|
||||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do
|
||||
{:ok, object.data}
|
||||
else
|
||||
_e -> user
|
||||
_ -> :error
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
end)
|
||||
|
||||
user =
|
||||
if locked = params["locked"] do
|
||||
with locked <- locked == "true",
|
||||
new_info <- Map.put(user.info, "locked", locked),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
info_cng = User.Info.mastodon_profile_update(user.info, info_params)
|
||||
|
||||
with changeset <- User.update_changeset(user, params),
|
||||
with changeset <- User.update_changeset(user, user_params),
|
||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
if original_user != user do
|
||||
CommonAPI.update(user)
|
||||
@ -453,40 +433,31 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
|> json([])
|
||||
end
|
||||
|
||||
def update_media(%{assigns: %{user: _}} = conn, data) do
|
||||
def update_media(%{assigns: %{user: user}} = conn, data) do
|
||||
with %Object{} = object <- Repo.get(Object, data["id"]),
|
||||
true <- Object.authorize_mutation(object, user),
|
||||
true <- is_binary(data["description"]),
|
||||
description <- data["description"] do
|
||||
new_data = %{object.data | "name" => description}
|
||||
|
||||
change = Object.change(object, %{data: new_data})
|
||||
{:ok, _} = Repo.update(change)
|
||||
{:ok, _} =
|
||||
object
|
||||
|> Object.change(%{data: new_data})
|
||||
|> Repo.update()
|
||||
|
||||
data =
|
||||
new_data
|
||||
|> Map.put("id", object.id)
|
||||
|
||||
render(conn, StatusView, "attachment.json", %{attachment: data})
|
||||
attachment_data = Map.put(new_data, "id", object.id)
|
||||
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
|
||||
end
|
||||
end
|
||||
|
||||
def upload(%{assigns: %{user: _}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <- ActivityPub.upload(file) do
|
||||
objdata =
|
||||
if Map.has_key?(data, "description") do
|
||||
Map.put(object.data, "name", data["description"])
|
||||
else
|
||||
object.data
|
||||
end
|
||||
|
||||
change = Object.change(object, %{data: objdata})
|
||||
{:ok, object} = Repo.update(change)
|
||||
|
||||
objdata =
|
||||
objdata
|
||||
|> Map.put("id", object.id)
|
||||
|
||||
render(conn, StatusView, "attachment.json", %{attachment: objdata})
|
||||
def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(file,
|
||||
actor: User.ap_id(user),
|
||||
description: Map.get(data, "description")
|
||||
) do
|
||||
attachment_data = Map.put(object.data, "id", object.id)
|
||||
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
|
||||
end
|
||||
end
|
||||
|
||||
@ -529,17 +500,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
# TODO: Pagination
|
||||
def followers(conn, %{"id" => id}) do
|
||||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||
with %User{} = user <- Repo.get(User, id),
|
||||
{:ok, followers} <- User.get_followers(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
|
||||
end
|
||||
end
|
||||
|
||||
def following(conn, %{"id" => id}) do
|
||||
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||
with %User{} = user <- Repo.get(User, id),
|
||||
{:ok, followers} <- User.get_friends(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
|
||||
end
|
||||
end
|
||||
@ -659,7 +643,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
|
||||
# TODO: Use proper query
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
with blocked_users <- user.info["blocks"] || [],
|
||||
with blocked_users <- user.info.blocks || [],
|
||||
accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do
|
||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||
json(conn, res)
|
||||
@ -667,7 +651,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
end
|
||||
|
||||
def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do
|
||||
json(conn, info["domain_blocks"] || [])
|
||||
json(conn, info.domain_blocks || [])
|
||||
end
|
||||
|
||||
def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
@ -915,11 +899,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
max_toot_chars: limit
|
||||
},
|
||||
rights: %{
|
||||
delete_others_notice: !!user.info["is_moderator"]
|
||||
delete_others_notice: !!user.info.is_moderator
|
||||
},
|
||||
compose: %{
|
||||
me: "#{user.id}",
|
||||
default_privacy: user.info["default_scope"] || "public",
|
||||
default_privacy: user.info.default_scope,
|
||||
default_sensitive: false
|
||||
},
|
||||
media_attachments: %{
|
||||
@ -939,7 +923,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
]
|
||||
},
|
||||
settings:
|
||||
Map.get(user.info, "settings") ||
|
||||
Map.get(user.info, :settings) ||
|
||||
%{
|
||||
onboarded: true,
|
||||
home: %{
|
||||
@ -1224,7 +1208,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
user = user.nickname
|
||||
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
|
||||
|
||||
with {:ok, %{status_code: 200, body: body}} <-
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
@httpoison.get(url, [], timeout: timeout, recv_timeout: timeout),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
data2 =
|
||||
|
@ -14,10 +14,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
image = User.avatar_url(user) |> MediaProxy.url()
|
||||
header = User.banner_url(user) |> MediaProxy.url()
|
||||
user_info = User.user_info(user)
|
||||
bot = (user.info["source_data"]["type"] || "Person") in ["Application", "Service"]
|
||||
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
|
||||
|
||||
emojis =
|
||||
(user.info["source_data"]["tag"] || [])
|
||||
(user.info.source_data["tag"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||
%{
|
||||
@ -29,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
end)
|
||||
|
||||
fields =
|
||||
(user.info["source_data"]["attachment"] || [])
|
||||
(user.info.source_data["attachment"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
|
@ -1,135 +1,34 @@
|
||||
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
||||
use Pleroma.Web, :controller
|
||||
require Logger
|
||||
alias Pleroma.{Web.MediaProxy, ReverseProxy}
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
@default_proxy_opts [max_body_length: 25 * 1_048_576]
|
||||
|
||||
@max_body_length 25 * 1_048_576
|
||||
|
||||
@cache_control %{
|
||||
default: "public, max-age=1209600",
|
||||
error: "public, must-revalidate, max-age=160"
|
||||
}
|
||||
|
||||
# Content-types that will not be returned as content-disposition attachments
|
||||
# Override with :media_proxy, :safe_content_types in the configuration
|
||||
@safe_content_types [
|
||||
"image/gif",
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
"audio/mpeg",
|
||||
"audio/mp3",
|
||||
"video/webm",
|
||||
"video/mp4"
|
||||
]
|
||||
|
||||
def remote(conn, params = %{"sig" => sig, "url" => url}) do
|
||||
config = Application.get_env(:pleroma, :media_proxy, [])
|
||||
|
||||
with true <- Keyword.get(config, :enabled, false),
|
||||
{:ok, url} <- Pleroma.Web.MediaProxy.decode_url(sig, url),
|
||||
def remote(conn, params = %{"sig" => sig64, "url" => url64}) do
|
||||
with config <- Pleroma.Config.get([:media_proxy], []),
|
||||
true <- Keyword.get(config, :enabled, false),
|
||||
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
||||
filename <- Path.basename(URI.parse(url).path),
|
||||
true <-
|
||||
if(Map.get(params, "filename"),
|
||||
do: filename == Path.basename(conn.request_path),
|
||||
else: true
|
||||
),
|
||||
{:ok, content_type, body} <- proxy_request(url),
|
||||
safe_content_type <-
|
||||
Enum.member?(
|
||||
Keyword.get(config, :safe_content_types, @safe_content_types),
|
||||
content_type
|
||||
) do
|
||||
conn
|
||||
|> put_resp_content_type(content_type)
|
||||
|> set_cache_header(:default)
|
||||
|> put_resp_header(
|
||||
"content-security-policy",
|
||||
"default-src 'none'; style-src 'unsafe-inline'; media-src data:; img-src 'self' data:"
|
||||
)
|
||||
|> put_resp_header("x-xss-protection", "1; mode=block")
|
||||
|> put_resp_header("x-content-type-options", "nosniff")
|
||||
|> put_attachement_header(safe_content_type, filename)
|
||||
|> send_resp(200, body)
|
||||
:ok <- filename_matches(Map.has_key?(params, "filename"), conn.request_path, url) do
|
||||
ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
|
||||
else
|
||||
false ->
|
||||
send_error(conn, 404)
|
||||
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
|
||||
|
||||
{:error, :invalid_signature} ->
|
||||
send_error(conn, 403)
|
||||
send_resp(conn, 403, Plug.Conn.Status.reason_phrase(403))
|
||||
|
||||
{:error, {:http, _, url}} ->
|
||||
redirect_or_error(conn, url, Keyword.get(config, :redirect_on_failure, true))
|
||||
{:wrong_filename, filename} ->
|
||||
redirect(conn, external: MediaProxy.build_url(sig64, url64, filename))
|
||||
end
|
||||
end
|
||||
|
||||
defp proxy_request(link) do
|
||||
headers = [
|
||||
{"user-agent",
|
||||
"Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{
|
||||
Application.get_env(:pleroma, :instance)[:email]
|
||||
}>"}
|
||||
]
|
||||
def filename_matches(has_filename, path, url) do
|
||||
filename = MediaProxy.filename(url)
|
||||
|
||||
options =
|
||||
@httpoison.process_request_options([:insecure, {:follow_redirect, true}]) ++
|
||||
[{:pool, :default}]
|
||||
|
||||
with {:ok, 200, headers, client} <- :hackney.request(:get, link, headers, "", options),
|
||||
headers = Enum.into(headers, Map.new()),
|
||||
{:ok, body} <- proxy_request_body(client),
|
||||
content_type <- proxy_request_content_type(headers, body) do
|
||||
{:ok, content_type, body}
|
||||
else
|
||||
{:ok, status, _, _} ->
|
||||
Logger.warn("MediaProxy: request failed, status #{status}, link: #{link}")
|
||||
{:error, {:http, :bad_status, link}}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.warn("MediaProxy: request failed, error #{inspect(error)}, link: #{link}")
|
||||
{:error, {:http, error, link}}
|
||||
cond do
|
||||
has_filename && filename && Path.basename(path) != filename -> {:wrong_filename, filename}
|
||||
true -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
defp set_cache_header(conn, key) do
|
||||
Plug.Conn.put_resp_header(conn, "cache-control", @cache_control[key])
|
||||
end
|
||||
|
||||
defp redirect_or_error(conn, url, true), do: redirect(conn, external: url)
|
||||
defp redirect_or_error(conn, url, _), do: send_error(conn, 502, "Media proxy error: " <> url)
|
||||
|
||||
defp send_error(conn, code, body \\ "") do
|
||||
conn
|
||||
|> set_cache_header(:error)
|
||||
|> send_resp(code, body)
|
||||
end
|
||||
|
||||
defp proxy_request_body(client), do: proxy_request_body(client, <<>>)
|
||||
|
||||
defp proxy_request_body(client, body) when byte_size(body) < @max_body_length do
|
||||
case :hackney.stream_body(client) do
|
||||
{:ok, data} -> proxy_request_body(client, <<body::binary, data::binary>>)
|
||||
:done -> {:ok, body}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp proxy_request_body(client, _) do
|
||||
:hackney.close(client)
|
||||
{:error, :body_too_large}
|
||||
end
|
||||
|
||||
# TODO: the body is passed here as well because some hosts do not provide a content-type.
|
||||
# At some point we may want to use magic numbers to discover the content-type and reply a proper one.
|
||||
defp proxy_request_content_type(headers, _body) do
|
||||
headers["Content-Type"] || headers["content-type"] || "application/octet-stream"
|
||||
end
|
||||
|
||||
defp put_attachement_header(conn, true, _), do: conn
|
||||
|
||||
defp put_attachement_header(conn, false, filename) do
|
||||
put_resp_header(conn, "content-disposition", "attachment; filename='#{filename}'")
|
||||
end
|
||||
end
|
||||
|
@ -17,10 +17,8 @@ defmodule Pleroma.Web.MediaProxy do
|
||||
base64 = Base.url_encode64(url, @base64_opts)
|
||||
sig = :crypto.hmac(:sha, secret, base64)
|
||||
sig64 = sig |> Base.url_encode64(@base64_opts)
|
||||
filename = if path = URI.parse(url).path, do: "/" <> Path.basename(path), else: ""
|
||||
|
||||
Keyword.get(config, :base_url, Pleroma.Web.base_url()) <>
|
||||
"/proxy/#{sig64}/#{base64}#{filename}"
|
||||
build_url(sig64, base64, filename(url))
|
||||
end
|
||||
end
|
||||
|
||||
@ -35,4 +33,20 @@ defmodule Pleroma.Web.MediaProxy do
|
||||
{:error, :invalid_signature}
|
||||
end
|
||||
end
|
||||
|
||||
def filename(url_or_path) do
|
||||
if path = URI.parse(url_or_path).path, do: Path.basename(path)
|
||||
end
|
||||
|
||||
def build_url(sig_base64, url_base64, filename \\ nil) do
|
||||
[
|
||||
Pleroma.Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()),
|
||||
"proxy",
|
||||
sig_base64,
|
||||
url_base64,
|
||||
filename
|
||||
]
|
||||
|> Enum.filter(fn value -> value end)
|
||||
|> Path.join()
|
||||
end
|
||||
end
|
||||
|
@ -226,25 +226,21 @@ defmodule Pleroma.Web.OStatus do
|
||||
old_data = %{
|
||||
avatar: user.avatar,
|
||||
bio: user.bio,
|
||||
name: user.name,
|
||||
info: user.info
|
||||
name: user.name
|
||||
}
|
||||
|
||||
with false <- user.local,
|
||||
avatar <- make_avatar_object(doc),
|
||||
bio <- string_from_xpath("//author[1]/summary", doc),
|
||||
name <- string_from_xpath("//author[1]/poco:displayName", doc),
|
||||
info <-
|
||||
Map.put(user.info, "banner", make_avatar_object(doc, "header") || user.info["banner"]),
|
||||
new_data <- %{
|
||||
avatar: avatar || old_data.avatar,
|
||||
name: name || old_data.name,
|
||||
bio: bio || old_data.bio,
|
||||
info: info || old_data.info
|
||||
bio: bio || old_data.bio
|
||||
},
|
||||
false <- new_data == old_data do
|
||||
change = Ecto.Changeset.change(user, new_data)
|
||||
Repo.update(change)
|
||||
User.update_and_set_cache(change)
|
||||
else
|
||||
_ ->
|
||||
{:ok, user}
|
||||
@ -350,13 +346,15 @@ defmodule Pleroma.Web.OStatus do
|
||||
|
||||
def fetch_activity_from_atom_url(url) do
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body, status_code: code}} when code in 200..299 <-
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
url,
|
||||
[Accept: "application/atom+xml"],
|
||||
follow_redirect: true,
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000
|
||||
]
|
||||
) do
|
||||
Logger.debug("Got document from #{url}, handling...")
|
||||
handle_incoming(body)
|
||||
|
@ -302,12 +302,6 @@ defmodule Pleroma.Web.Router do
|
||||
post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner)
|
||||
post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background)
|
||||
|
||||
post(
|
||||
"/account/most_recent_notification",
|
||||
TwitterAPI.Controller,
|
||||
:update_most_recent_notification
|
||||
)
|
||||
|
||||
get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline)
|
||||
get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline)
|
||||
get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline)
|
||||
@ -335,6 +329,7 @@ defmodule Pleroma.Web.Router do
|
||||
|
||||
post("/statusnet/media/upload", TwitterAPI.Controller, :upload)
|
||||
post("/media/upload", TwitterAPI.Controller, :upload_json)
|
||||
post("/media/metadata/create", TwitterAPI.Controller, :update_media)
|
||||
|
||||
post("/favorites/create/:id", TwitterAPI.Controller, :favorite)
|
||||
post("/favorites/create", TwitterAPI.Controller, :favorite)
|
||||
|
@ -157,15 +157,17 @@ defmodule Pleroma.Web.Salmon do
|
||||
|> Enum.filter(fn user -> user && !user.local end)
|
||||
end
|
||||
|
||||
defp send_to_user(%{info: %{"salmon" => salmon}}, feed, poster) do
|
||||
with {:ok, %{status_code: code}} <-
|
||||
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do
|
||||
with {:ok, %{status: code}} <-
|
||||
poster.(
|
||||
salmon,
|
||||
feed,
|
||||
[{"Content-Type", "application/magic-envelope+xml"}],
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
hackney: [pool: :default]
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
pool: :default
|
||||
]
|
||||
) do
|
||||
Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
|
||||
else
|
||||
@ -185,7 +187,7 @@ defmodule Pleroma.Web.Salmon do
|
||||
]
|
||||
def publish(user, activity, poster \\ &@httpoison.post/4)
|
||||
|
||||
def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||
when type in @supported_activities do
|
||||
feed = ActivityRepresenter.to_simple_form(activity, user, true)
|
||||
|
||||
|
@ -188,7 +188,7 @@ defmodule Pleroma.Web.Streamer do
|
||||
# Get the current user so we have up-to-date blocks etc.
|
||||
if socket.assigns[:user] do
|
||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||
blocks = user.info["blocks"] || []
|
||||
blocks = user.info.blocks || []
|
||||
|
||||
parent = Object.normalize(item.data["object"])
|
||||
|
||||
@ -206,7 +206,7 @@ defmodule Pleroma.Web.Streamer do
|
||||
# Get the current user so we have up-to-date blocks etc.
|
||||
if socket.assigns[:user] do
|
||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||
blocks = user.info["blocks"] || []
|
||||
blocks = user.info.blocks || []
|
||||
|
||||
unless item.actor in blocks do
|
||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||
|
@ -93,11 +93,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
end
|
||||
end
|
||||
|
||||
def upload(%Plug.Upload{} = file, format \\ "xml") do
|
||||
{:ok, object} = ActivityPub.upload(file)
|
||||
def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do
|
||||
{:ok, object} = ActivityPub.upload(file, actor: User.ap_id(user))
|
||||
|
||||
url = List.first(object.data["url"])
|
||||
href = url["href"] |> MediaProxy.url()
|
||||
href = url["href"]
|
||||
type = url["mediaType"]
|
||||
|
||||
case format do
|
||||
@ -132,7 +132,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
params = %{
|
||||
nickname: params["nickname"],
|
||||
name: params["fullname"],
|
||||
bio: params["bio"],
|
||||
bio: User.parse_bio(params["bio"]),
|
||||
email: params["email"],
|
||||
password: params["password"],
|
||||
password_confirmation: params["confirm"]
|
||||
@ -148,7 +148,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
|
||||
cond do
|
||||
registrations_open || (!is_nil(token) && !token.used) ->
|
||||
changeset = User.register_changeset(%User{}, params)
|
||||
changeset = User.register_changeset(%User{info: %{}}, params)
|
||||
|
||||
with {:ok, user} <- Repo.insert(changeset) do
|
||||
!registrations_open && UserInviteToken.mark_as_used(token.token)
|
||||
@ -279,14 +279,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
|
||||
def get_external_profile(for_user, uri) do
|
||||
with %User{} = user <- User.get_or_fetch(uri) do
|
||||
spawn(fn ->
|
||||
with url <- user.info["topic"],
|
||||
{:ok, %{body: body}} <-
|
||||
@httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000) do
|
||||
OStatus.handle_incoming(body)
|
||||
end
|
||||
end)
|
||||
|
||||
{:ok, UserView.render("show.json", %{user: user, for: for_user})}
|
||||
else
|
||||
_e ->
|
||||
|
@ -4,7 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||
alias Pleroma.{Repo, Activity, User, Notification}
|
||||
alias Pleroma.{Repo, Activity, Object, User, Notification}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Ecto.Changeset
|
||||
@ -226,16 +226,51 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
end
|
||||
end
|
||||
|
||||
def upload(conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media)
|
||||
@doc """
|
||||
Updates metadata of uploaded media object.
|
||||
Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
|
||||
"""
|
||||
def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
|
||||
object = Repo.get(Object, id)
|
||||
description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
|
||||
|
||||
{conn, status, response_body} =
|
||||
cond do
|
||||
!object ->
|
||||
{halt(conn), :not_found, ""}
|
||||
|
||||
!Object.authorize_mutation(object, user) ->
|
||||
{halt(conn), :forbidden, "You can only update your own uploads."}
|
||||
|
||||
!is_binary(description) ->
|
||||
{conn, :not_modified, ""}
|
||||
|
||||
true ->
|
||||
new_data = Map.put(object.data, "name", description)
|
||||
|
||||
{:ok, _} =
|
||||
object
|
||||
|> Object.change(%{data: new_data})
|
||||
|> Repo.update()
|
||||
|
||||
{conn, :no_content, ""}
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_status(status)
|
||||
|> json(response_body)
|
||||
end
|
||||
|
||||
def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media, user)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/atom+xml")
|
||||
|> send_resp(200, response)
|
||||
end
|
||||
|
||||
def upload_json(conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media, "json")
|
||||
def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media, user, "json")
|
||||
|
||||
conn
|
||||
|> json_reply(200, response)
|
||||
@ -290,11 +325,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
end
|
||||
|
||||
def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
||||
upload_limit =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.fetch(:avatar_upload_limit)
|
||||
|
||||
{:ok, object} = ActivityPub.upload(params, upload_limit)
|
||||
{:ok, object} = ActivityPub.upload(params, type: :avatar)
|
||||
change = Changeset.change(user, %{avatar: object.data})
|
||||
{:ok, user} = User.update_and_set_cache(change)
|
||||
CommonAPI.update(user)
|
||||
@ -303,14 +334,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
end
|
||||
|
||||
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||
upload_limit =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.fetch(:banner_upload_limit)
|
||||
|
||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, upload_limit),
|
||||
new_info <- Map.put(user.info, "banner", object.data),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||
new_info <- %{"banner" => object.data},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
CommonAPI.update(user)
|
||||
%{"url" => [%{"href" => href} | _]} = object.data
|
||||
response = %{url: href} |> Jason.encode!()
|
||||
@ -321,14 +349,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
end
|
||||
|
||||
def update_background(%{assigns: %{user: user}} = conn, params) do
|
||||
upload_limit =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.fetch(:background_upload_limit)
|
||||
|
||||
with {:ok, object} <- ActivityPub.upload(params, upload_limit),
|
||||
new_info <- Map.put(user.info, "background", object.data),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, _user} <- User.update_and_set_cache(change) do
|
||||
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
||||
new_info <- %{"background" => object.data},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
%{"url" => [%{"href" => href} | _]} = object.data
|
||||
response = %{url: href} |> Jason.encode!()
|
||||
|
||||
@ -350,32 +375,32 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
end
|
||||
end
|
||||
|
||||
def update_most_recent_notification(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with id when is_number(id) <- String.to_integer(id),
|
||||
info <- user.info,
|
||||
mrn <- max(id, user.info["most_recent_notification"] || 0),
|
||||
updated_info <- Map.put(info, "most_recent_notification", mrn),
|
||||
changeset <- User.info_changeset(user, %{info: updated_info}),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
conn
|
||||
|> json_reply(200, Jason.encode!(mrn))
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't update.")
|
||||
end
|
||||
end
|
||||
|
||||
def followers(conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||
def followers(%{assigns: %{user: for_user}} = conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(for_user, params),
|
||||
{:ok, followers} <- User.get_followers(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]})
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't get followers")
|
||||
end
|
||||
end
|
||||
|
||||
def friends(conn, params) do
|
||||
def friends(%{assigns: %{user: for_user}} = conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||
{:ok, friends} <- User.get_friends(user) do
|
||||
friends =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> friends
|
||||
user.info.hide_network -> []
|
||||
true -> friends
|
||||
end
|
||||
|
||||
render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]})
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't get friends")
|
||||
@ -451,67 +476,41 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||
json(conn, [])
|
||||
end
|
||||
|
||||
defp build_info_cng(user, params) do
|
||||
info_params =
|
||||
["no_rich_text", "locked", "hide_network"]
|
||||
|> Enum.reduce(%{}, fn key, res ->
|
||||
if value = params[key] do
|
||||
Map.put(res, key, value == "true")
|
||||
else
|
||||
res
|
||||
end
|
||||
end)
|
||||
|
||||
info_params =
|
||||
if value = params["default_scope"] do
|
||||
Map.put(info_params, "default_scope", value)
|
||||
else
|
||||
info_params
|
||||
end
|
||||
|
||||
User.Info.profile_update(user.info, info_params)
|
||||
end
|
||||
|
||||
defp parse_profile_bio(user, params) do
|
||||
if bio = params["description"] do
|
||||
Map.put(params, "bio", User.parse_bio(bio, user))
|
||||
else
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
def update_profile(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
if bio = params["description"] do
|
||||
mentions = Formatter.parse_mentions(bio)
|
||||
tags = Formatter.parse_tags(bio)
|
||||
|
||||
emoji =
|
||||
(user.info["source_data"]["tag"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||
{String.trim(name, ":"), url}
|
||||
end)
|
||||
|
||||
bio_html = CommonUtils.format_input(bio, mentions, tags, "text/plain")
|
||||
Map.put(params, "bio", bio_html |> Formatter.emojify(emoji))
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
user =
|
||||
if locked = params["locked"] do
|
||||
with locked <- locked == "true",
|
||||
new_info <- Map.put(user.info, "locked", locked),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
|
||||
user =
|
||||
if no_rich_text = params["no_rich_text"] do
|
||||
with no_rich_text <- no_rich_text == "true",
|
||||
new_info <- Map.put(user.info, "no_rich_text", no_rich_text),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
|
||||
user =
|
||||
if default_scope = params["default_scope"] do
|
||||
with new_info <- Map.put(user.info, "default_scope", default_scope),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
params = parse_profile_bio(user, params)
|
||||
info_cng = build_info_cng(user, params)
|
||||
|
||||
with changeset <- User.update_changeset(user, params),
|
||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
CommonAPI.update(user)
|
||||
render(conn, UserView, "user.json", %{user: user, for: user})
|
||||
|
@ -31,7 +31,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
||||
user_info = User.get_cached_user_info(user)
|
||||
|
||||
emoji =
|
||||
(user.info["source_data"]["tag"] || [])
|
||||
(user.info.source_data["tag"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||
{String.trim(name, ":"), url}
|
||||
@ -40,7 +40,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
||||
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
||||
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
||||
fields =
|
||||
(user.info["source_data"]["attachment"] || [])
|
||||
(user.info.source_data["attachment"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
@ -66,17 +66,17 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
||||
"profile_image_url_profile_size" => image,
|
||||
"profile_image_url_original" => image,
|
||||
"rights" => %{
|
||||
"delete_others_notice" => !!user.info["is_moderator"]
|
||||
"delete_others_notice" => !!user.info.is_moderator
|
||||
},
|
||||
"screen_name" => user.nickname,
|
||||
"statuses_count" => user_info[:note_count],
|
||||
"statusnet_profile_url" => user.ap_id,
|
||||
"cover_photo" => User.banner_url(user) |> MediaProxy.url(),
|
||||
"background_image" => image_url(user.info["background"]) |> MediaProxy.url(),
|
||||
"background_image" => image_url(user.info.background) |> MediaProxy.url(),
|
||||
"is_local" => user.local,
|
||||
"locked" => !!user.info["locked"],
|
||||
"default_scope" => user.info["default_scope"] || "public",
|
||||
"no_rich_text" => user.info["no_rich_text"] || false,
|
||||
"locked" => user.info.locked,
|
||||
"default_scope" => user.info.default_scope,
|
||||
"no_rich_text" => user.info.no_rich_text,
|
||||
"fields" => fields
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ defmodule Pleroma.Web.WebFinger do
|
||||
|
||||
def represent_user(user, "JSON") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"])
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info.keys)
|
||||
magic_key = Salmon.encode_key(public)
|
||||
|
||||
%{
|
||||
@ -83,7 +83,7 @@ defmodule Pleroma.Web.WebFinger do
|
||||
|
||||
def represent_user(user, "XML") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"])
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info.keys)
|
||||
magic_key = Salmon.encode_key(public)
|
||||
|
||||
{
|
||||
@ -113,16 +113,22 @@ defmodule Pleroma.Web.WebFinger do
|
||||
|
||||
# This seems a better fit in Salmon
|
||||
def ensure_keys_present(user) do
|
||||
info = user.info || %{}
|
||||
info = user.info
|
||||
|
||||
if info["keys"] do
|
||||
if info.keys do
|
||||
{:ok, user}
|
||||
else
|
||||
{:ok, pem} = Salmon.generate_rsa_pem()
|
||||
info = Map.put(info, "keys", pem)
|
||||
|
||||
Ecto.Changeset.change(user, info: info)
|
||||
|> User.update_and_set_cache()
|
||||
info_cng =
|
||||
info
|
||||
|> Pleroma.User.Info.set_keys(pem)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
User.update_and_set_cache(cng)
|
||||
end
|
||||
end
|
||||
|
||||
@ -214,7 +220,7 @@ defmodule Pleroma.Web.WebFinger do
|
||||
end
|
||||
|
||||
def find_lrdd_template(domain) do
|
||||
with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <-
|
||||
with {:ok, %{status: status, body: body}} when status in 200..299 <-
|
||||
@httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do
|
||||
get_template_from_xml(body)
|
||||
else
|
||||
@ -253,7 +259,7 @@ defmodule Pleroma.Web.WebFinger do
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
),
|
||||
{:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response do
|
||||
{:ok, %{status: status, body: body}} when status in 200..299 <- response do
|
||||
doc = XML.parse_document(body)
|
||||
|
||||
if doc != :error do
|
||||
|
@ -146,7 +146,7 @@ defmodule Pleroma.Web.Websub do
|
||||
end
|
||||
|
||||
def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
|
||||
topic = subscribed.info["topic"]
|
||||
topic = subscribed.info.topic
|
||||
# FIXME: Race condition, use transactions
|
||||
{:ok, subscription} =
|
||||
with subscription when not is_nil(subscription) <-
|
||||
@ -158,7 +158,7 @@ defmodule Pleroma.Web.Websub do
|
||||
_e ->
|
||||
subscription = %WebsubClientSubscription{
|
||||
topic: topic,
|
||||
hub: subscribed.info["hub"],
|
||||
hub: subscribed.info.hub,
|
||||
subscribers: [subscriber.ap_id],
|
||||
state: "requested",
|
||||
secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(),
|
||||
@ -173,7 +173,7 @@ defmodule Pleroma.Web.Websub do
|
||||
|
||||
def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
|
||||
with {:ok, response} <- getter.(topic),
|
||||
status_code when status_code in 200..299 <- response.status_code,
|
||||
status when status in 200..299 <- response.status,
|
||||
body <- response.body,
|
||||
doc <- XML.parse_document(body),
|
||||
uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc),
|
||||
@ -221,7 +221,7 @@ defmodule Pleroma.Web.Websub do
|
||||
|
||||
task = Task.async(websub_checker)
|
||||
|
||||
with {:ok, %{status_code: 202}} <-
|
||||
with {:ok, %{status: 202}} <-
|
||||
poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"),
|
||||
{:ok, websub} <- Task.yield(task, timeout) do
|
||||
{:ok, websub}
|
||||
@ -257,7 +257,7 @@ defmodule Pleroma.Web.Websub do
|
||||
signature = sign(secret || "", xml)
|
||||
Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
|
||||
|
||||
with {:ok, %{status_code: code}} <-
|
||||
with {:ok, %{status: code}} <-
|
||||
@httpoison.post(
|
||||
callback,
|
||||
xml,
|
||||
@ -265,9 +265,11 @@ defmodule Pleroma.Web.Websub do
|
||||
{"Content-Type", "application/atom+xml"},
|
||||
{"X-Hub-Signature", "sha1=#{signature}"}
|
||||
],
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
hackney: [pool: :default]
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
pool: :default
|
||||
]
|
||||
) do
|
||||
Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
|
||||
{:ok, code}
|
||||
|
1
mix.exs
1
mix.exs
@ -56,6 +56,7 @@ defmodule Pleroma.Mixfile do
|
||||
{:calendar, "~> 0.17.4"},
|
||||
{:cachex, "~> 3.0.2"},
|
||||
{:httpoison, "~> 1.2.0"},
|
||||
{:tesla, "~> 1.2"},
|
||||
{:jason, "~> 1.0"},
|
||||
{:mogrify, "~> 0.6.1"},
|
||||
{:ex_aws, "~> 2.0"},
|
||||
|
1
mix.lock
1
mix.lock
@ -49,6 +49,7 @@
|
||||
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
|
||||
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, 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", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
||||
|
@ -0,0 +1,7 @@
|
||||
defmodule Pleroma.Repo.Migrations.AddUUIDExtension do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
execute("create extension if not exists \"uuid-ossp\"")
|
||||
end
|
||||
end
|
@ -0,0 +1,7 @@
|
||||
defmodule Pleroma.Repo.Migrations.AddUUIDsToUserInfo do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
execute("update users set info = jsonb_set(info, '{\"id\"}', to_jsonb(uuid_generate_v4()))")
|
||||
end
|
||||
end
|
@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.0808aeafc6252b3050ea95b17dcaff1a.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.34667c2817916147413f.js></script><script type=text/javascript src=/static/js/vendor.32c621c7157f34c20923.js></script><script type=text/javascript src=/static/js/app.065638d22ade92dea420.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.0808aeafc6252b3050ea95b17dcaff1a.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.18df0da570d88ba76ec5.js></script><script type=text/javascript src=/static/js/vendor.0e895ca116d5ba12f2b6.js></script><script type=text/javascript src=/static/js/app.3f7c9aaedc6b87fa9653.js></script></body></html>
|
@ -11,6 +11,8 @@
|
||||
"scopeOptionsEnabled": false,
|
||||
"formattingOptionsEnabled": false,
|
||||
"collapseMessageWithSubject": false,
|
||||
"scopeCopy": false,
|
||||
"subjectLineBehavior": "email",
|
||||
"hidePostStats": false,
|
||||
"hideUserStats": false,
|
||||
"loginMethod": "password"
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11
priv/static/static/js/app.3f7c9aaedc6b87fa9653.js
Normal file
11
priv/static/static/js/app.3f7c9aaedc6b87fa9653.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.3f7c9aaedc6b87fa9653.js.map
Normal file
1
priv/static/static/js/app.3f7c9aaedc6b87fa9653.js.map
Normal file
File diff suppressed because one or more lines are too long
2
priv/static/static/js/manifest.18df0da570d88ba76ec5.js
Normal file
2
priv/static/static/js/manifest.18df0da570d88ba76ec5.js
Normal file
@ -0,0 +1,2 @@
|
||||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"0e895ca116d5ba12f2b6",2:"3f7c9aaedc6b87fa9653"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.18df0da570d88ba76ec5.js.map
|
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],a[l]&&i.push.apply(i,a[l]),a[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(r&&r(c,o);i.length;)i.shift().call(null,t);if(o[0])return n[0]=0,t(0)};var n={},a={0:0};t.e=function(e,r){if(0===a[e])return r.call(null,t);if(void 0!==a[e])a[e].push(r);else{a[e]=[r];var n=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"32c621c7157f34c20923",2:"065638d22ade92dea420"}[e]+".js",n.appendChild(c)}},t.m=e,t.c=n,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.34667c2817916147413f.js.map
|
34
priv/static/static/js/vendor.0e895ca116d5ba12f2b6.js
Normal file
34
priv/static/static/js/vendor.0e895ca116d5ba12f2b6.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/vendor.0e895ca116d5ba12f2b6.js.map
Normal file
1
priv/static/static/js/vendor.0e895ca116d5ba12f2b6.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -53,4 +53,19 @@ defmodule Pleroma.ConfigTest do
|
||||
assert Pleroma.Config.get([:instance, :config_test]) == true
|
||||
assert Pleroma.Config.get([:instance, :config_nested_test, :x]) == true
|
||||
end
|
||||
|
||||
test "delete/1 with a key" do
|
||||
Pleroma.Config.put([:delete_me], :delete_me)
|
||||
Pleroma.Config.delete([:delete_me])
|
||||
assert Pleroma.Config.get([:delete_me]) == nil
|
||||
end
|
||||
|
||||
test "delete/2 with a list of keys" do
|
||||
Pleroma.Config.put([:delete_me], hello: "world", world: "Hello")
|
||||
Pleroma.Config.delete([:delete_me, :world])
|
||||
assert Pleroma.Config.get([:delete_me]) == [hello: "world"]
|
||||
Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello")
|
||||
Pleroma.Config.delete([:delete_me, :delete_me, :world])
|
||||
assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"]
|
||||
end
|
||||
end
|
||||
|
2
test/fixtures/httpoison_mock/framatube.org_host_meta
vendored
Normal file
2
test/fixtures/httpoison_mock/framatube.org_host_meta
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">framatube.org</hm:Host><Link rel="lrdd" template="http://framatube.org/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
|
10
test/fixtures/httpoison_mock/gerzilla.de_host_meta
vendored
Normal file
10
test/fixtures/httpoison_mock/gerzilla.de_host_meta
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'
|
||||
xmlns:hm='http://host-meta.net/xrd/1.0'>
|
||||
|
||||
<hm:Host>gerzilla.de</hm:Host>
|
||||
|
||||
<Link rel='lrdd' type="application/xrd+xml" template='https://gerzilla.de/xrd/?uri={uri}' />
|
||||
<Link rel="http://oexchange.org/spec/0.8/rel/resident-target" type="application/xrd+xml"
|
||||
href="https://gerzilla.de/oexchange/xrd" />
|
||||
|
||||
</XRD>
|
2
test/fixtures/httpoison_mock/gnusocial.de_host_meta
vendored
Normal file
2
test/fixtures/httpoison_mock/gnusocial.de_host_meta
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">gnusocial.de</hm:Host><Link rel="lrdd" template="http://gnusocial.de/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
|
@ -5,12 +5,17 @@ defmodule Pleroma.FormatterTest do
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe ".add_hashtag_links" do
|
||||
test "turns hashtags into links" do
|
||||
text = "I love #cofe and #2hu"
|
||||
|
||||
expected_text =
|
||||
"I love <a href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
|
||||
"I love <a data-tag='cofe' href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
|
||||
|
||||
tags = Formatter.parse_tags(text)
|
||||
|
||||
@ -110,7 +115,7 @@ defmodule Pleroma.FormatterTest do
|
||||
archaeme =
|
||||
insert(:user, %{
|
||||
nickname: "archaeme",
|
||||
info: %{"source_data" => %{"url" => "https://archeme/@archaeme"}}
|
||||
info: %Pleroma.User.Info{source_data: %{"url" => "https://archeme/@archaeme"}}
|
||||
})
|
||||
|
||||
archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
|
||||
@ -123,11 +128,11 @@ defmodule Pleroma.FormatterTest do
|
||||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text =
|
||||
"<span><a class='mention' href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a class='mention' href='#{
|
||||
"https://archeme/@archaeme"
|
||||
}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a class='mention' href='#{
|
||||
archaeme_remote.ap_id
|
||||
}'>@<span>archaeme</span></a></span>"
|
||||
"<span><a data-user='#{gsimg.id}' class='mention' href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a data-user='#{
|
||||
archaeme.id
|
||||
}' class='mention' href='#{"https://archeme/@archaeme"}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a data-user='#{
|
||||
archaeme_remote.id
|
||||
}' class='mention' href='#{archaeme_remote.ap_id}'>@<span>archaeme</span></a></span>"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
@ -145,7 +150,7 @@ defmodule Pleroma.FormatterTest do
|
||||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text =
|
||||
"<span><a class='mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test"
|
||||
"<span><a data-user='#{mike.id}' class='mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
@ -161,7 +166,9 @@ defmodule Pleroma.FormatterTest do
|
||||
assert length(subs) == 1
|
||||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text = "<span><a class='mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi"
|
||||
expected_text =
|
||||
"<span><a data-user='#{o.id}' class='mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
|
||||
|
55
test/http_test.exs
Normal file
55
test/http_test.exs
Normal file
@ -0,0 +1,55 @@
|
||||
defmodule Pleroma.HTTPTest do
|
||||
use Pleroma.DataCase
|
||||
import Tesla.Mock
|
||||
|
||||
setup do
|
||||
mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/hello",
|
||||
headers: [{"content-type", "application/json"}]
|
||||
} ->
|
||||
json(%{"my" => "data"})
|
||||
|
||||
%{method: :get, url: "http://example.com/hello"} ->
|
||||
%Tesla.Env{status: 200, body: "hello"}
|
||||
|
||||
%{method: :post, url: "http://example.com/world"} ->
|
||||
%Tesla.Env{status: 200, body: "world"}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "get/1" do
|
||||
test "returns successfully result" do
|
||||
assert Pleroma.HTTP.get("http://example.com/hello") == {
|
||||
:ok,
|
||||
%Tesla.Env{status: 200, body: "hello"}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "get/2 (with headers)" do
|
||||
test "returns successfully result for json content-type" do
|
||||
assert Pleroma.HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) ==
|
||||
{
|
||||
:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: "{\"my\":\"data\"}",
|
||||
headers: [{"content-type", "application/json"}]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "post/2" do
|
||||
test "returns successfully result" do
|
||||
assert Pleroma.HTTP.post("http://example.com/world", "") == {
|
||||
:ok,
|
||||
%Tesla.Env{status: 200, body: "world"}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
@ -82,6 +82,23 @@ defmodule Pleroma.MediaProxyTest do
|
||||
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
|
||||
assert decode_url(sig, base64) == {:error, :invalid_signature}
|
||||
end
|
||||
|
||||
test "uses the configured base_url" do
|
||||
base_url = Pleroma.Config.get([:media_proxy, :base_url])
|
||||
|
||||
if base_url do
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:media_proxy, :base_url], base_url)
|
||||
end)
|
||||
end
|
||||
|
||||
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
||||
|
||||
url = "https://pleroma.soykaf.com/static/logo.png"
|
||||
encoded = url(url)
|
||||
|
||||
assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url]))
|
||||
end
|
||||
end
|
||||
|
||||
describe "when disabled" do
|
||||
|
56
test/plugs/oauth_plug_test.exs
Normal file
56
test/plugs/oauth_plug_test.exs
Normal file
@ -0,0 +1,56 @@
|
||||
defmodule Pleroma.Plugs.OAuthPlugTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Plugs.OAuthPlug
|
||||
import Pleroma.Factory
|
||||
|
||||
@session_opts [
|
||||
store: :cookie,
|
||||
key: "_test",
|
||||
signing_salt: "cooldude"
|
||||
]
|
||||
|
||||
setup %{conn: conn} do
|
||||
user = insert(:user)
|
||||
{:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create_token(insert(:oauth_app), user)
|
||||
%{user: user, token: token, conn: conn}
|
||||
end
|
||||
|
||||
test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "BEARER #{opts[:token]}")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
|
||||
test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "bearer #{opts[:token]}")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
|
||||
test "with invalid token, it not assigns the user", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "bearer TTTTT")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
refute conn.assigns[:user]
|
||||
end
|
||||
|
||||
test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> Plug.Session.call(Plug.Session.init(@session_opts))
|
||||
|> fetch_session()
|
||||
|> put_session(:oauth_token, opts[:token])
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
end
|
@ -13,7 +13,7 @@ defmodule Pleroma.Plugs.UserEnabledPlugTest do
|
||||
end
|
||||
|
||||
test "with a user that is deactivated, it removes that user", %{conn: conn} do
|
||||
user = insert(:user, info: %{"deactivated" => true})
|
||||
user = insert(:user, info: %{deactivated: true})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
@ -5,7 +5,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
|
||||
import Pleroma.Factory
|
||||
|
||||
test "accepts a user that is admin", %{conn: conn} do
|
||||
user = insert(:user, info: %{"is_admin" => true})
|
||||
user = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|
@ -36,6 +36,23 @@ defmodule Pleroma.DataCase do
|
||||
:ok
|
||||
end
|
||||
|
||||
def ensure_local_uploader(_context) do
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
filters = Pleroma.Config.get([Pleroma.Upload, :filters])
|
||||
|
||||
unless uploader == Pleroma.Uploaders.Local || filters != [] do
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], [])
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], uploader)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], filters)
|
||||
end)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
A helper that transform changeset errors to a map of messages.
|
||||
|
||||
|
@ -7,7 +7,8 @@ defmodule Pleroma.Factory do
|
||||
email: sequence(:email, &"user#{&1}@example.com"),
|
||||
nickname: sequence(:nickname, &"nick#{&1}"),
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||
bio: sequence(:bio, &"Tester Number #{&1}")
|
||||
bio: sequence(:bio, &"Tester Number #{&1}"),
|
||||
info: %{}
|
||||
}
|
||||
|
||||
%{
|
||||
|
675
test/support/http_request_mock.ex
Normal file
675
test/support/http_request_mock.ex
Normal file
@ -0,0 +1,675 @@
|
||||
defmodule HttpRequestMock do
|
||||
require Logger
|
||||
|
||||
def request(
|
||||
%Tesla.Env{
|
||||
url: url,
|
||||
method: method,
|
||||
headers: headers,
|
||||
query: query,
|
||||
body: body
|
||||
} = _env
|
||||
) do
|
||||
with {:ok, res} <- apply(__MODULE__, method, [url, query, body, headers]) do
|
||||
res
|
||||
else
|
||||
{_, r} = error ->
|
||||
# Logger.warn(r)
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
# GET Requests
|
||||
#
|
||||
def get(url, query \\ [], body \\ [], headers \\ [])
|
||||
|
||||
def get("https://osada.macgirvin.com/channel/mike", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/29191",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://pawoo.net/users/pekorino.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pawoo.net/.well-known/webfinger?resource=acct:https://pawoo.net/users/pekorino",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=acct:https://social.stopwatchingus-heidelberg.de/user/18330",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mamot.fr/users/Skruyb.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mamot.fr/.well-known/webfinger?resource=acct:https://mamot.fr/users/Skruyb",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=nonexistant@social.heldscal.la",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://squeet.me/xrd/?uri=lain@squeet.me", _, _,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mst3k.interlinked.me/users/luciferMysticus", _, _,
|
||||
Accept: "application/activity+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://prismo.news/@mxb", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___prismo.news__mxb.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://hubzilla.example.org/channel/kaniini", _, _,
|
||||
Accept: "application/activity+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://niu.moe/users/rye", _, _, Accept: "application/activity+json") do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/rye.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.example.org/users/admin/statuses/100787282858396771", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://puckipedia.com/", _, _, Accept: "application/activity+json") do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/puckipedia.com.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://peertube.moe/accounts/7even", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7even.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/peertube.moe-vid.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://baptiste.gelez.xyz/@/BaptisteGelez", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.example.org/users/admin", _, _, Accept: "application/activity+json") do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.example.org/@admin/99541947525187367", _, _,
|
||||
Accept: "application/activity+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/mastodon-note-object.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/notice/7369654", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7369654.html")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mstdn.io/users/mayuutann", _, _, Accept: "application/activity+json") do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mayumayu.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mstdn.io/users/mayuutann/statuses/99568293732299394", _, _,
|
||||
Accept: "application/activity+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://pleroma.soykaf.com/users/lain/feed.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(url, _, _, Accept: "application/xrd+xml,application/jrd+json")
|
||||
when url in [
|
||||
"https://pleroma.soykaf.com/.well-known/webfinger?resource=acct:https://pleroma.soykaf.com/users/lain",
|
||||
"https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/lain"
|
||||
] do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/notice/2827873", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/show/2827873.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://testing.pleroma.lol/objects/b319022a-4946-44c5-9de9-34801f95507b", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://shitposter.club/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/show/7369654.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7369654.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/notice/4027863", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7369654.html")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.sakamoto.gq/users/eal/feed.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://social.sakamoto.gq/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.sakamoto.gq/.well-known/webfinger?resource=https://social.sakamoto.gq/users/eal",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", _, _,
|
||||
Accept: "application/atom+xml"
|
||||
) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom")}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.social/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/lambadalambda",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gs.example.org/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"http://gs.example.org/.well-known/webfinger?resource=http://gs.example.org:4040/index.php/user/1",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://squeet.me/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta")}}
|
||||
end
|
||||
|
||||
def get("https://squeet.me/xrd?uri=lain@squeet.me", _, _,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la",
|
||||
_,
|
||||
_,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://framatube.org/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/framatube.org_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://framatube.org/main/xrd?uri=framasoft@framatube.org", _, _,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/json"}],
|
||||
body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gnusocial.de/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/gnusocial.de_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gnusocial.de/main/xrd?uri=winterdienst@gnusocial.de", _, _,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://status.alpicola.com/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/status.alpicola.com_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://macgirvin.com/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gerzilla.de/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/gerzilla.de_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://gerzilla.de/xrd/?uri=kaniini@gerzilla.de", _, _,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/json"}],
|
||||
body: File.read!("test/fixtures/httpoison_mock/kaniini@gerzilla.de.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/23211",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://social.heldscal.la/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mastodon.social/users/lambadalambda.atom", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.atom")}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
||||
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
||||
end
|
||||
|
||||
def get(url, query, body, headers) do
|
||||
{:error,
|
||||
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
|
||||
# POST Requests
|
||||
#
|
||||
|
||||
def post(url, query \\ [], body \\ [], headers \\ [])
|
||||
|
||||
def post("http://example.org/needs_refresh", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: ""
|
||||
}}
|
||||
end
|
||||
|
||||
def post(url, _query, _body, _headers) do
|
||||
{:error, "Not implemented the mock response for post #{inspect(url)}"}
|
||||
end
|
||||
end
|
@ -1,881 +0,0 @@
|
||||
defmodule HTTPoisonMock do
|
||||
alias HTTPoison.Response
|
||||
|
||||
def get(url, body \\ [], headers \\ [])
|
||||
|
||||
def get("https://prismo.news/@mxb", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___prismo.news__mxb.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://osada.macgirvin.com/channel/mike", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com",
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://info.pleroma.site/activity.json", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https__info.pleroma.site_activity.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://info.pleroma.site/activity2.json", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https__info.pleroma.site_activity2.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://info.pleroma.site/activity3.json", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https__info.pleroma.site_activity3.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://info.pleroma.site/activity4.json", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https__info.pleroma.site_activity4.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://info.pleroma.site/actor.json", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___info.pleroma.site_actor.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://puckipedia.com/", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/puckipedia.com.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://gerzilla.de/.well-known/webfinger?resource=acct:kaniini@gerzilla.de",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/kaniini@gerzilla.de.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://framatube.org/.well-known/webfinger?resource=acct:framasoft@framatube.org",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://gnusocial.de/.well-known/webfinger?resource=acct:winterdienst@gnusocial.de",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "nonexistant@social.heldscal.la"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 500,
|
||||
body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "shp@social.heldscal.la"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://social.heldscal.la/user/23211"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/23211",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://social.heldscal.la/user/29191"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/29191",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mastodon.social/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://mastodon.social/users/lambadalambda"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/lambadalambda",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://shitposter.club/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://shitposter.club/user/1"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"http://gs.example.org/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "http://gs.example.org:4040/index.php/user/1"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"http://gs.example.org/.well-known/webfinger?resource=http://gs.example.org:4040/index.php/user/1",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=https://social.stopwatchingus-heidelberg.de/user/18330",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pleroma.soykaf.com/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://pleroma.soykaf.com/users/lain"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/lain",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mastodon.social/users/lambadalambda.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom",
|
||||
_body,
|
||||
_headers
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://pleroma.soykaf.com/users/lain/feed.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://social.sakamoto.gq/users/eal/feed.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/notice/2827873", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/show/2827873.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def post(
|
||||
"https://social.heldscal.la/main/push/hub",
|
||||
{:form, _data},
|
||||
"Content-type": "application/x-www-form-urlencoded"
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 202
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.example.org/users/admin/statuses/100787282858396771", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!(
|
||||
"test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json"
|
||||
)
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pawoo.net/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://pawoo.net/users/pekorino"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pawoo.net/.well-known/webfinger?resource=https://pawoo.net/users/pekorino",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://pawoo.net/users/pekorino.atom", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mamot.fr/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://mamot.fr/users/Skruyb"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.sakamoto.gq/.well-known/webfinger",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
params: [resource: "https://social.sakamoto.gq/users/eal"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.sakamoto.gq/.well-known/webfinger?resource=https://social.sakamoto.gq/users/eal",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/shp",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://squeet.me/xrd/?uri=lain@squeet.me",
|
||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mamot.fr/users/Skruyb.atom", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056",
|
||||
[Accept: "application/atom+xml"],
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://pleroma.soykaf.com/users/shp/feed.atom", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://social.heldscal.la/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://status.alpicola.com/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/status.alpicola.com_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://macgirvin.com/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.social/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://shitposter.club/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://pleroma.soykaf.com/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://social.sakamoto.gq/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://gs.example.org/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://pawoo.net/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/pawoo.net_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mamot.fr/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mamot.fr_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.xyz/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mastodon.xyz_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://social.wxcafe.net/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/social.wxcafe.net_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://squeet.me/.well-known/host-meta", [], follow_redirect: true) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"http://social.stopwatchingus-heidelberg.de/.well-known/host-meta",
|
||||
[],
|
||||
follow_redirect: true
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://mastodon.example.org/users/admin", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://hubzilla.example.org/channel/kaniini",
|
||||
[Accept: "application/activity+json"],
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/hellpie.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://niu.moe/users/rye", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/rye.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://n1u.moe/users/rye", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/rye.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mst3k.interlinked.me/users/luciferMysticus",
|
||||
[Accept: "application/activity+json"],
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://mstdn.io/users/mayuutann", [Accept: "application/activity+json"], _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mayumayu.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"http://mastodon.example.org/@admin/99541947525187367",
|
||||
[Accept: "application/activity+json"],
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/mastodon-note-object.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mstdn.io/users/mayuutann/statuses/99568293732299394",
|
||||
[Accept: "application/activity+json"],
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/notice/7369654", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7369654.html")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://shitposter.club/api/statuses/show/7369654.atom", _body, _headers) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7369654.atom")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://baptiste.gelez.xyz/@/BaptisteGelez", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/peertube.moe-vid.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://peertube.moe/accounts/7even", _, _) do
|
||||
{:ok,
|
||||
%Response{
|
||||
status_code: 200,
|
||||
body: File.read!("test/fixtures/httpoison_mock/7even.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get(url, body, headers) do
|
||||
{:error,
|
||||
"Not implemented the mock response for get #{inspect(url)}, #{inspect(body)}, #{
|
||||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
|
||||
def post(url, _body, _headers) do
|
||||
{:error, "Not implemented the mock response for post #{inspect(url)}"}
|
||||
end
|
||||
|
||||
def post(url, _body, _headers, _options) do
|
||||
{:error, "Not implemented the mock response for post #{inspect(url)}"}
|
||||
end
|
||||
end
|
@ -2,7 +2,43 @@ defmodule Pleroma.UploadTest do
|
||||
alias Pleroma.Upload
|
||||
use Pleroma.DataCase
|
||||
|
||||
describe "Storing a file" do
|
||||
describe "Storing a file with the Local uploader" do
|
||||
setup [:ensure_local_uploader]
|
||||
|
||||
test "returns a media url" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
|
||||
assert %{"url" => [%{"href" => url}]} = data
|
||||
|
||||
assert String.starts_with?(url, Pleroma.Web.base_url() <> "/media/")
|
||||
end
|
||||
|
||||
test "returns a media url with configured base_url" do
|
||||
base_url = "https://cache.pleroma.social"
|
||||
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file, base_url: base_url)
|
||||
|
||||
assert %{"url" => [%{"href" => url}]} = data
|
||||
|
||||
assert String.starts_with?(url, base_url <> "/media/")
|
||||
end
|
||||
|
||||
test "copies the file to the configured folder with deduping" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
@ -12,10 +48,11 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file, true)
|
||||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
|
||||
|
||||
assert data["name"] ==
|
||||
"e7a6d0cf595bff76f14c9a98b6c199539559e8b844e02e51e5efcfd1f614a2df.jpeg"
|
||||
assert List.first(data["url"])["href"] ==
|
||||
Pleroma.Web.base_url() <>
|
||||
"/media/e7a6d0cf595bff76f14c9a98b6c199539559e8b844e02e51e5efcfd1f614a2df.jpg"
|
||||
end
|
||||
|
||||
test "copies the file to the configured folder without deduping" do
|
||||
@ -27,7 +64,7 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file, false)
|
||||
{:ok, data} = Upload.store(file)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
||||
@ -40,7 +77,7 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
data = Upload.store(file, true)
|
||||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
|
||||
assert hd(data["url"])["mediaType"] == "image/jpeg"
|
||||
end
|
||||
|
||||
@ -53,7 +90,7 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "an [image"
|
||||
}
|
||||
|
||||
data = Upload.store(file, false)
|
||||
{:ok, data} = Upload.store(file)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
||||
@ -66,7 +103,7 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "an [image.blah"
|
||||
}
|
||||
|
||||
data = Upload.store(file, false)
|
||||
{:ok, data} = Upload.store(file)
|
||||
assert data["name"] == "an [image.jpg"
|
||||
end
|
||||
|
||||
@ -79,8 +116,22 @@ defmodule Pleroma.UploadTest do
|
||||
filename: "test.txt"
|
||||
}
|
||||
|
||||
data = Upload.store(file, false)
|
||||
{:ok, data} = Upload.store(file)
|
||||
assert data["name"] == "test.txt"
|
||||
end
|
||||
|
||||
test "copies the file to the configured folder with anonymizing filename" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "an [image.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.AnonymizeFilename])
|
||||
|
||||
refute data["name"] == "an [image.jpg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -9,6 +9,11 @@ defmodule Pleroma.UserTest do
|
||||
import Pleroma.Factory
|
||||
import Ecto.Query
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "ap_id returns the activity pub id for the user" do
|
||||
user = UserBuilder.build()
|
||||
|
||||
@ -34,14 +39,14 @@ defmodule Pleroma.UserTest do
|
||||
user = Repo.get(User, user.id)
|
||||
|
||||
followed = User.get_by_ap_id(followed.ap_id)
|
||||
assert followed.info["follower_count"] == 1
|
||||
assert followed.info.follower_count == 1
|
||||
|
||||
assert User.ap_followers(followed) in user.following
|
||||
end
|
||||
|
||||
test "can't follow a deactivated users" do
|
||||
user = insert(:user)
|
||||
followed = insert(:user, info: %{"deactivated" => true})
|
||||
followed = insert(:user, info: %{deactivated: true})
|
||||
|
||||
{:error, _} = User.follow(user, followed)
|
||||
end
|
||||
@ -56,8 +61,8 @@ defmodule Pleroma.UserTest do
|
||||
end
|
||||
|
||||
test "local users do not automatically follow local locked accounts" do
|
||||
follower = insert(:user, info: %{"locked" => true})
|
||||
followed = insert(:user, info: %{"locked" => true})
|
||||
follower = insert(:user, info: %{locked: true})
|
||||
followed = insert(:user, info: %{locked: true})
|
||||
|
||||
{:ok, follower} = User.maybe_direct_follow(follower, followed)
|
||||
|
||||
@ -144,6 +149,18 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||
end
|
||||
|
||||
test "it ensures info is not nil" do
|
||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||
|
||||
assert changeset.valid?
|
||||
|
||||
{:ok, user} =
|
||||
changeset
|
||||
|> Repo.insert()
|
||||
|
||||
refute is_nil(user.info)
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetching a user from nickname or trying to build one" do
|
||||
@ -185,12 +202,14 @@ defmodule Pleroma.UserTest do
|
||||
local: false,
|
||||
nickname: "admin@mastodon.example.org",
|
||||
ap_id: "http://mastodon.example.org/users/admin",
|
||||
last_refreshed_at: a_week_ago
|
||||
last_refreshed_at: a_week_ago,
|
||||
info: %{}
|
||||
)
|
||||
|
||||
assert orig_user.last_refreshed_at == a_week_ago
|
||||
|
||||
user = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
|
||||
assert user.info.source_data["endpoints"]
|
||||
|
||||
refute user.last_refreshed_at == orig_user.last_refreshed_at
|
||||
end
|
||||
@ -311,45 +330,45 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
user = User.get_by_ap_id(note.data["actor"])
|
||||
|
||||
assert user.info["note_count"] == nil
|
||||
assert user.info.note_count == 0
|
||||
|
||||
{:ok, user} = User.update_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
end
|
||||
|
||||
test "it increases the info->note_count property" do
|
||||
note = insert(:note)
|
||||
user = User.get_by_ap_id(note.data["actor"])
|
||||
|
||||
assert user.info["note_count"] == nil
|
||||
assert user.info.note_count == 0
|
||||
|
||||
{:ok, user} = User.increase_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
|
||||
{:ok, user} = User.increase_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 2
|
||||
assert user.info.note_count == 2
|
||||
end
|
||||
|
||||
test "it decreases the info->note_count property" do
|
||||
note = insert(:note)
|
||||
user = User.get_by_ap_id(note.data["actor"])
|
||||
|
||||
assert user.info["note_count"] == nil
|
||||
assert user.info.note_count == 0
|
||||
|
||||
{:ok, user} = User.increase_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
|
||||
{:ok, user} = User.decrease_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 0
|
||||
assert user.info.note_count == 0
|
||||
|
||||
{:ok, user} = User.decrease_note_count(user)
|
||||
|
||||
assert user.info["note_count"] == 0
|
||||
assert user.info.note_count == 0
|
||||
end
|
||||
|
||||
test "it sets the info->follower_count property" do
|
||||
@ -358,11 +377,11 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
User.follow(follower, user)
|
||||
|
||||
assert user.info["follower_count"] == nil
|
||||
assert user.info.follower_count == 0
|
||||
|
||||
{:ok, user} = User.update_follower_count(user)
|
||||
|
||||
assert user.info["follower_count"] == 1
|
||||
assert user.info.follower_count == 1
|
||||
end
|
||||
end
|
||||
|
||||
@ -489,11 +508,11 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
test ".deactivate can de-activate then re-activate a user" do
|
||||
user = insert(:user)
|
||||
assert false == !!user.info["deactivated"]
|
||||
assert false == user.info.deactivated
|
||||
{:ok, user} = User.deactivate(user)
|
||||
assert true == user.info["deactivated"]
|
||||
assert true == user.info.deactivated
|
||||
{:ok, user} = User.deactivate(user, false)
|
||||
assert false == !!user.info["deactivated"]
|
||||
assert false == user.info.deactivated
|
||||
end
|
||||
|
||||
test ".delete deactivates a user, all follow relationships and all create activities" do
|
||||
@ -517,7 +536,7 @@ defmodule Pleroma.UserTest do
|
||||
follower = Repo.get(User, follower.id)
|
||||
user = Repo.get(User, user.id)
|
||||
|
||||
assert user.info["deactivated"]
|
||||
assert user.info.deactivated
|
||||
|
||||
refute User.following?(user, followed)
|
||||
refute User.following?(followed, follower)
|
||||
@ -546,7 +565,7 @@ defmodule Pleroma.UserTest do
|
||||
end
|
||||
|
||||
test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
|
||||
user = insert(:user, %{info: %{"no_rich_text" => true}})
|
||||
user = insert(:user, %{info: %{no_rich_text: true}})
|
||||
|
||||
assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
|
||||
end
|
||||
|
@ -5,6 +5,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||
alias Pleroma.{Repo, User}
|
||||
alias Pleroma.Activity
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "/relay" do
|
||||
test "with the relay active, it returns the relay user", %{conn: conn} do
|
||||
res =
|
||||
@ -145,6 +150,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||
assert result["first"]["orderedItems"] == [user.ap_id]
|
||||
end
|
||||
|
||||
test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
user_two = insert(:user, %{info: %{hide_network: true}})
|
||||
User.follow(user, user_two)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/users/#{user_two.nickname}/followers")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == []
|
||||
assert result["totalItems"] == 1
|
||||
end
|
||||
|
||||
test "it works for more than 10 users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
@ -186,6 +205,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||
assert result["first"]["orderedItems"] == [user_two.ap_id]
|
||||
end
|
||||
|
||||
test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: true}})
|
||||
user_two = insert(:user)
|
||||
User.follow(user, user_two)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/users/#{user.nickname}/following")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == []
|
||||
assert result["totalItems"] == 1
|
||||
end
|
||||
|
||||
test "it works for more than 10 users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
@ -7,6 +7,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
alias Pleroma.Builders.ActivityBuilder
|
||||
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
|
||||
setup do
|
||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "building a user from his ap id" do
|
||||
test "it returns a user" do
|
||||
@ -14,8 +20,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
assert user.ap_id == user_id
|
||||
assert user.nickname == "admin@mastodon.example.org"
|
||||
assert user.info["source_data"]
|
||||
assert user.info["ap_enabled"]
|
||||
assert user.info.source_data
|
||||
assert user.info.ap_enabled
|
||||
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
|
||||
end
|
||||
end
|
||||
|
@ -12,6 +12,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it ignores an incoming notice if we already have it" do
|
||||
activity = insert(:note_activity)
|
||||
@ -92,7 +97,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
user = User.get_by_ap_id(object["actor"])
|
||||
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
end
|
||||
|
||||
test "it works for incoming notices with hashtags" do
|
||||
@ -307,7 +312,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
}
|
||||
]
|
||||
|
||||
assert user.info["banner"]["url"] == [
|
||||
assert user.info.banner["url"] == [
|
||||
%{
|
||||
"href" =>
|
||||
"https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||
@ -337,7 +342,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
user = User.get_cached_by_ap_id(data["actor"])
|
||||
assert user.info["locked"] == true
|
||||
assert user.info.locked == true
|
||||
end
|
||||
|
||||
test "it works for incoming deletes" do
|
||||
@ -543,7 +548,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it works for incoming accepts which were orphaned" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
{:ok, follow_activity} = ActivityPub.follow(follower, followed)
|
||||
|
||||
@ -565,7 +570,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it works for incoming accepts which are referenced by IRI only" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
{:ok, follow_activity} = ActivityPub.follow(follower, followed)
|
||||
|
||||
@ -585,7 +590,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it fails for incoming accepts which cannot be correlated" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-accept-activity.json")
|
||||
@ -604,7 +609,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it fails for incoming rejects which cannot be correlated" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-reject-activity.json")
|
||||
@ -623,7 +628,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it works for incoming rejects which are orphaned" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
{:ok, follower} = User.follow(follower, followed)
|
||||
{:ok, _follow_activity} = ActivityPub.follow(follower, followed)
|
||||
@ -648,7 +653,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
|
||||
test "it works for incoming rejects which are referenced by IRI only" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, %{info: %{"locked" => true}})
|
||||
followed = insert(:user, %{info: %User.Info{locked: true}})
|
||||
|
||||
{:ok, follower} = User.follow(follower, followed)
|
||||
{:ok, follow_activity} = ActivityPub.follow(follower, followed)
|
||||
@ -815,18 +820,18 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
|
||||
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
|
||||
assert user.info["ap_enabled"]
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.ap_enabled
|
||||
assert user.info.note_count == 1
|
||||
assert user.follower_address == "https://niu.moe/users/rye/followers"
|
||||
|
||||
# Wait for the background task
|
||||
:timer.sleep(1000)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.info["note_count"] == 1
|
||||
assert user.info.note_count == 1
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
assert user.follower_address in activity.recipients
|
||||
@ -847,7 +852,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||
"https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||
}
|
||||
]
|
||||
} = user.info["banner"]
|
||||
} = user.info.banner
|
||||
|
||||
refute "..." in activity.recipients
|
||||
|
||||
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
|
||||
describe "/api/pleroma/admin/user" do
|
||||
test "Delete" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
@ -21,7 +21,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
end
|
||||
|
||||
test "Create" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
@ -39,7 +39,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
|
||||
describe "/api/pleroma/admin/permission_group" do
|
||||
test "GET is giving user_info" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
@ -47,33 +47,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> get("/api/pleroma/admin/permission_group/#{admin.nickname}")
|
||||
|
||||
assert json_response(conn, 200) == admin.info
|
||||
assert json_response(conn, 200) == %{
|
||||
"is_admin" => true,
|
||||
"is_moderator" => false
|
||||
}
|
||||
end
|
||||
|
||||
test "/:right POST, can add to a permission group" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
user_info =
|
||||
user.info
|
||||
|> Map.put("is_admin", true)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> post("/api/pleroma/admin/permission_group/#{user.nickname}/admin")
|
||||
|
||||
assert json_response(conn, 200) == user_info
|
||||
assert json_response(conn, 200) == %{
|
||||
"is_admin" => true
|
||||
}
|
||||
end
|
||||
|
||||
test "/:right DELETE, can remove from a permission group" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
user = insert(:user, info: %{"is_admin" => true})
|
||||
|
||||
user_info =
|
||||
user.info
|
||||
|> Map.put("is_admin", false)
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
@ -81,12 +78,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/permission_group/#{user.nickname}/admin")
|
||||
|
||||
assert json_response(conn, 200) == user_info
|
||||
assert json_response(conn, 200) == %{
|
||||
"is_admin" => false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
test "/api/pleroma/admin/invite_token" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
@ -98,8 +97,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||
end
|
||||
|
||||
test "/api/pleroma/admin/password_reset" do
|
||||
admin = insert(:user, info: %{"is_admin" => true})
|
||||
user = insert(:user, info: %{"is_admin" => true})
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user