@@ -113,6 +113,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation. | |||
- Pleroma API: Email change endpoint. | |||
- Admin API: Added moderation log | |||
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache). | |||
- Web response cache (currently, enabled for ActivityPub) | |||
- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`) | |||
- ActivityPub: Add ActivityPub actor's `discoverable` parameter. | |||
@@ -591,6 +591,8 @@ config :pleroma, :rate_limit, nil | |||
config :pleroma, Pleroma.ActivityExpiration, enabled: true | |||
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false | |||
config :pleroma, :web_cache_ttl, | |||
activity_pub: nil, | |||
activity_pub_question: 30_000 | |||
@@ -2687,6 +2687,42 @@ config :pleroma, :config_description, [ | |||
} | |||
] | |||
}, | |||
%{ | |||
group: :pleroma, | |||
key: Pleroma.Plugs.RemoteIp, | |||
type: :group, | |||
description: """ | |||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** | |||
`Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
""", | |||
children: [ | |||
%{ | |||
key: :enabled, | |||
type: :boolean, | |||
description: "Enable/disable the plug. Defaults to `false`.", | |||
suggestions: [true, false] | |||
}, | |||
%{ | |||
key: :headers, | |||
type: {:list, :string}, | |||
description: | |||
"A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`." | |||
}, | |||
%{ | |||
key: :proxies, | |||
type: {:list, :string}, | |||
description: | |||
"A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`." | |||
}, | |||
%{ | |||
key: :reserved, | |||
type: {:list, :string}, | |||
description: | |||
"Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network)." | |||
} | |||
] | |||
}, | |||
%{ | |||
group: :pleroma, | |||
key: :web_cache_ttl, | |||
@@ -730,6 +730,8 @@ This will probably take a long time. | |||
This is an advanced feature and disabled by default. | |||
If your instance is behind a reverse proxy you must enable and configure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip). | |||
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: | |||
* The first element: `scale` (Integer). The time scale in milliseconds. | |||
@@ -756,3 +758,16 @@ Available caches: | |||
* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration). | |||
* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds). | |||
## Pleroma.Plugs.RemoteIp | |||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** | |||
`Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
Available options: | |||
* `enabled` - Enable/disable the plug. Defaults to `false`. | |||
* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`. | |||
* `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`. | |||
* `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network). |
@@ -70,6 +70,7 @@ server { | |||
proxy_set_header Upgrade $http_upgrade; | |||
proxy_set_header Connection "upgrade"; | |||
proxy_set_header Host $http_host; | |||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only | |||
# and `localhost.` resolves to [::0] on some systems: see issue #930 | |||
@@ -0,0 +1,54 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Plugs.RemoteIp do | |||
@moduledoc """ | |||
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
""" | |||
@behaviour Plug | |||
@headers ~w[ | |||
forwarded | |||
x-forwarded-for | |||
x-client-ip | |||
x-real-ip | |||
] | |||
# https://en.wikipedia.org/wiki/Localhost | |||
# https://en.wikipedia.org/wiki/Private_network | |||
@reserved ~w[ | |||
127.0.0.0/8 | |||
::1/128 | |||
fc00::/7 | |||
10.0.0.0/8 | |||
172.16.0.0/12 | |||
192.168.0.0/16 | |||
] | |||
def init(_), do: nil | |||
def call(conn, _) do | |||
config = Pleroma.Config.get(__MODULE__, []) | |||
if Keyword.get(config, :enabled, false) do | |||
RemoteIp.call(conn, remote_ip_opts(config)) | |||
else | |||
conn | |||
end | |||
end | |||
defp remote_ip_opts(config) do | |||
headers = config |> Keyword.get(:headers, @headers) |> MapSet.new() | |||
reserved = Keyword.get(config, :reserved, @reserved) | |||
proxies = | |||
config | |||
|> Keyword.get(:proxies, []) | |||
|> Enum.concat(reserved) | |||
|> Enum.map(&InetCidr.parse/1) | |||
{headers, proxies} | |||
end | |||
end |
@@ -40,11 +40,11 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do | |||
|> put_params(params) | |||
|> status() | |||
|> summary() | |||
|> with_valid(&attachments/1) | |||
|> full_payload() | |||
|> expires_at() | |||
|> poll() | |||
|> with_valid(&in_reply_to/1) | |||
|> with_valid(&attachments/1) | |||
|> with_valid(&in_reply_to_conversation/1) | |||
|> with_valid(&visibility/1) | |||
|> content() | |||
@@ -98,10 +98,7 @@ defmodule Pleroma.Web.Endpoint do | |||
extra: extra | |||
) | |||
# Note: the plug and its configuration is compile-time this can't be upstreamed yet | |||
if proxies = Pleroma.Config.get([__MODULE__, :reverse_proxies]) do | |||
plug(RemoteIp, proxies: proxies) | |||
end | |||
plug(Pleroma.Plugs.RemoteIp) | |||
defmodule Instrumenter do | |||
use Prometheus.PhoenixInstrumenter | |||
@@ -0,0 +1,26 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.DomainBlockController do | |||
use Pleroma.Web, :controller | |||
alias Pleroma.User | |||
@doc "GET /api/v1/domain_blocks" | |||
def index(%{assigns: %{user: %{info: info}}} = conn, _) do | |||
json(conn, Map.get(info, :domain_blocks, [])) | |||
end | |||
@doc "POST /api/v1/domain_blocks" | |||
def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do | |||
User.block_domain(blocker, domain) | |||
json(conn, %{}) | |||
end | |||
@doc "DELETE /api/v1/domain_blocks" | |||
def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do | |||
User.unblock_domain(blocker, domain) | |||
json(conn, %{}) | |||
end | |||
end |
@@ -0,0 +1,72 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.FilterController do | |||
use Pleroma.Web, :controller | |||
alias Pleroma.Filter | |||
@doc "GET /api/v1/filters" | |||
def index(%{assigns: %{user: user}} = conn, _) do | |||
filters = Filter.get_filters(user) | |||
render(conn, "filters.json", filters: filters) | |||
end | |||
@doc "POST /api/v1/filters" | |||
def create( | |||
%{assigns: %{user: user}} = conn, | |||
%{"phrase" => phrase, "context" => context} = params | |||
) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
phrase: phrase, | |||
context: context, | |||
hide: Map.get(params, "irreversible", false), | |||
whole_word: Map.get(params, "boolean", true) | |||
# expires_at | |||
} | |||
{:ok, response} = Filter.create(query) | |||
render(conn, "filter.json", filter: response) | |||
end | |||
@doc "GET /api/v1/filters/:id" | |||
def show(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do | |||
filter = Filter.get(filter_id, user) | |||
render(conn, "filter.json", filter: filter) | |||
end | |||
@doc "PUT /api/v1/filters/:id" | |||
def update( | |||
%{assigns: %{user: user}} = conn, | |||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params | |||
) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
filter_id: filter_id, | |||
phrase: phrase, | |||
context: context, | |||
hide: Map.get(params, "irreversible", nil), | |||
whole_word: Map.get(params, "boolean", true) | |||
# expires_at | |||
} | |||
{:ok, response} = Filter.update(query) | |||
render(conn, "filter.json", filter: response) | |||
end | |||
@doc "DELETE /api/v1/filters/:id" | |||
def delete(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
filter_id: filter_id | |||
} | |||
{:ok, _} = Filter.delete(query) | |||
json(conn, %{}) | |||
end | |||
end |
@@ -0,0 +1,49 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do | |||
use Pleroma.Web, :controller | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView) | |||
plug(:assign_follower when action != :index) | |||
action_fallback(:errors) | |||
@doc "GET /api/v1/follow_requests" | |||
def index(%{assigns: %{user: followed}} = conn, _params) do | |||
follow_requests = User.get_follow_requests(followed) | |||
render(conn, "accounts.json", for: followed, users: follow_requests, as: :user) | |||
end | |||
@doc "POST /api/v1/follow_requests/:id/authorize" | |||
def authorize(%{assigns: %{user: followed, follower: follower}} = conn, _params) do | |||
with {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do | |||
render(conn, "relationship.json", user: followed, target: follower) | |||
end | |||
end | |||
@doc "POST /api/v1/follow_requests/:id/reject" | |||
def reject(%{assigns: %{user: followed, follower: follower}} = conn, _params) do | |||
with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do | |||
render(conn, "relationship.json", user: followed, target: follower) | |||
end | |||
end | |||
defp assign_follower(%{params: %{"id" => id}} = conn, _) do | |||
case User.get_cached_by_id(id) do | |||
%User{} = follower -> assign(conn, :follower, follower) | |||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() | |||
end | |||
end | |||
defp errors(conn, {:error, message}) do | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end |
@@ -14,13 +14,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Config | |||
alias Pleroma.Conversation.Participation | |||
alias Pleroma.Emoji | |||
alias Pleroma.Filter | |||
alias Pleroma.HTTP | |||
alias Pleroma.Object | |||
alias Pleroma.Pagination | |||
alias Pleroma.Plugs.RateLimiter | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Stats | |||
alias Pleroma.User | |||
alias Pleroma.Web | |||
@@ -30,12 +28,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MastodonAPI.AppView | |||
alias Pleroma.Web.MastodonAPI.ConversationView | |||
alias Pleroma.Web.MastodonAPI.FilterView | |||
alias Pleroma.Web.MastodonAPI.ListView | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
alias Pleroma.Web.MastodonAPI.MastodonView | |||
alias Pleroma.Web.MastodonAPI.ReportView | |||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.MediaProxy | |||
alias Pleroma.Web.OAuth.App | |||
@@ -396,55 +392,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do | |||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do | |||
conn | |||
|> add_link_headers(scheduled_activities) | |||
|> put_view(ScheduledActivityView) | |||
|> render("index.json", %{scheduled_activities: scheduled_activities}) | |||
end | |||
end | |||
def show_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do | |||
with %ScheduledActivity{} = scheduled_activity <- | |||
ScheduledActivity.get(user, scheduled_activity_id) do | |||
conn | |||
|> put_view(ScheduledActivityView) | |||
|> render("show.json", %{scheduled_activity: scheduled_activity}) | |||
else | |||
_ -> {:error, :not_found} | |||
end | |||
end | |||
def update_scheduled_status( | |||
%{assigns: %{user: user}} = conn, | |||
%{"id" => scheduled_activity_id} = params | |||
) do | |||
with %ScheduledActivity{} = scheduled_activity <- | |||
ScheduledActivity.get(user, scheduled_activity_id), | |||
{:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do | |||
conn | |||
|> put_view(ScheduledActivityView) | |||
|> render("show.json", %{scheduled_activity: scheduled_activity}) | |||
else | |||
nil -> {:error, :not_found} | |||
error -> error | |||
end | |||
end | |||
def delete_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do | |||
with %ScheduledActivity{} = scheduled_activity <- | |||
ScheduledActivity.get(user, scheduled_activity_id), | |||
{:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do | |||
conn | |||
|> put_view(ScheduledActivityView) | |||
|> render("show.json", %{scheduled_activity: scheduled_activity}) | |||
else | |||
nil -> {:error, :not_found} | |||
error -> error | |||
end | |||
end | |||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
id = List.wrap(id) | |||
q = from(u in User, where: u.id in ^id) | |||
@@ -550,42 +497,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do | |||
follow_requests = User.get_follow_requests(followed) | |||
conn | |||
|> put_view(AccountView) | |||
|> render("accounts.json", %{for: followed, users: follow_requests, as: :user}) | |||
end | |||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do | |||
with %User{} = follower <- User.get_cached_by_id(id), | |||
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: followed, target: follower}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do | |||
with %User{} = follower <- User.get_cached_by_id(id), | |||
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: followed, target: follower}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do | |||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)}, | |||
{_, true} <- {:followed, follower.id != followed.id}, | |||
@@ -715,20 +626,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do | |||
json(conn, info.domain_blocks || []) | |||
end | |||
def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do | |||
User.block_domain(blocker, domain) | |||
json(conn, %{}) | |||
end | |||
def unblock_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do | |||
User.unblock_domain(blocker, domain) | |||
json(conn, %{}) | |||
end | |||
def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %User{} = subscription_target <- User.get_cached_by_id(id), | |||
{:ok, subscription_target} = User.subscribe(user, subscription_target) do | |||
@@ -1040,65 +937,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
json(conn, %{}) | |||
end | |||
def get_filters(%{assigns: %{user: user}} = conn, _) do | |||
filters = Filter.get_filters(user) | |||
res = FilterView.render("filters.json", filters: filters) | |||
json(conn, res) | |||
end | |||
def create_filter( | |||
%{assigns: %{user: user}} = conn, | |||
%{"phrase" => phrase, "context" => context} = params | |||
) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
phrase: phrase, | |||
context: context, | |||
hide: Map.get(params, "irreversible", false), | |||
whole_word: Map.get(params, "boolean", true) | |||
# expires_at | |||
} | |||
{:ok, response} = Filter.create(query) | |||
res = FilterView.render("filter.json", filter: response) | |||
json(conn, res) | |||
end | |||
def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do | |||
filter = Filter.get(filter_id, user) | |||
res = FilterView.render("filter.json", filter: filter) | |||
json(conn, res) | |||
end | |||
def update_filter( | |||
%{assigns: %{user: user}} = conn, | |||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params | |||
) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
filter_id: filter_id, | |||
phrase: phrase, | |||
context: context, | |||
hide: Map.get(params, "irreversible", nil), | |||
whole_word: Map.get(params, "boolean", true) | |||
# expires_at | |||
} | |||
{:ok, response} = Filter.update(query) | |||
res = FilterView.render("filter.json", filter: response) | |||
json(conn, res) | |||
end | |||
def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do | |||
query = %Filter{ | |||
user_id: user.id, | |||
filter_id: filter_id | |||
} | |||
{:ok, _} = Filter.delete(query) | |||
json(conn, %{}) | |||
end | |||
def suggestions(%{assigns: %{user: user}} = conn, _) do | |||
suggestions = Config.get(:suggestions) | |||
@@ -0,0 +1,51 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
plug(:assign_scheduled_activity when action != :index) | |||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController) | |||
@doc "GET /api/v1/scheduled_statuses" | |||
def index(%{assigns: %{user: user}} = conn, params) do | |||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do | |||
conn | |||
|> add_link_headers(scheduled_activities) | |||
|> render("index.json", scheduled_activities: scheduled_activities) | |||
end | |||
end | |||
@doc "GET /api/v1/scheduled_statuses/:id" | |||
def show(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do | |||
render(conn, "show.json", scheduled_activity: scheduled_activity) | |||
end | |||
@doc "PUT /api/v1/scheduled_statuses/:id" | |||
def update(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, params) do | |||
with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do | |||
render(conn, "show.json", scheduled_activity: scheduled_activity) | |||
end | |||
end | |||
@doc "DELETE /api/v1/scheduled_statuses/:id" | |||
def delete(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do | |||
with {:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do | |||
render(conn, "show.json", scheduled_activity: scheduled_activity) | |||
end | |||
end | |||
defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do | |||
case ScheduledActivity.get(user, id) do | |||
%ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity) | |||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() | |||
end | |||
end | |||
end |
@@ -103,6 +103,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do | |||
end | |||
end | |||
def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do | |||
create(conn, Map.put(params, "status", "")) | |||
end | |||
@doc "GET /api/v1/statuses/:id" | |||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %Activity{} = activity <- Activity.get_by_id_with_object(id), | |||
@@ -7,11 +7,10 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
def render("index.json", %{scheduled_activities: scheduled_activities}) do | |||
render_many(scheduled_activities, ScheduledActivityView, "show.json") | |||
render_many(scheduled_activities, __MODULE__, "show.json") | |||
end | |||
def render("show.json", %{scheduled_activity: %ScheduledActivity{} = scheduled_activity}) do | |||
@@ -24,12 +23,8 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do | |||
end | |||
defp with_media_attachments(data, %{params: %{"media_attachments" => media_attachments}}) do | |||
try do | |||
attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment) | |||
Map.put(data, :media_attachments, attachments) | |||
rescue | |||
_ -> data | |||
end | |||
attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment) | |||
Map.put(data, :media_attachments, attachments) | |||
end | |||
defp with_media_attachments(data, _), do: data | |||
@@ -45,13 +40,9 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do | |||
in_reply_to_id: params["in_reply_to_id"] | |||
} | |||
data = | |||
if media_ids = params["media_ids"] do | |||
Map.put(data, :media_ids, media_ids) | |||
else | |||
data | |||
end | |||
data | |||
case params["media_ids"] do | |||
nil -> data | |||
media_ids -> Map.put(data, :media_ids, media_ids) | |||
end | |||
end | |||
end |
@@ -323,7 +323,7 @@ defmodule Pleroma.Web.Router do | |||
get("/accounts/:id/lists", MastodonAPIController, :account_lists) | |||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) | |||
get("/follow_requests", MastodonAPIController, :follow_requests) | |||
get("/follow_requests", FollowRequestController, :index) | |||
get("/blocks", MastodonAPIController, :blocks) | |||
get("/mutes", MastodonAPIController, :mutes) | |||
@@ -339,16 +339,16 @@ defmodule Pleroma.Web.Router do | |||
post("/notifications/dismiss", NotificationController, :dismiss) | |||
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) | |||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses) | |||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) | |||
get("/scheduled_statuses", ScheduledActivityController, :index) | |||
get("/scheduled_statuses/:id", ScheduledActivityController, :show) | |||
get("/lists", ListController, :index) | |||
get("/lists/:id", ListController, :show) | |||
get("/lists/:id/accounts", ListController, :list_accounts) | |||
get("/domain_blocks", MastodonAPIController, :domain_blocks) | |||
get("/domain_blocks", DomainBlockController, :index) | |||
get("/filters", MastodonAPIController, :get_filters) | |||
get("/filters", FilterController, :index) | |||
get("/suggestions", MastodonAPIController, :suggestions) | |||
@@ -377,8 +377,8 @@ defmodule Pleroma.Web.Router do | |||
post("/statuses/:id/mute", StatusController, :mute_conversation) | |||
post("/statuses/:id/unmute", StatusController, :unmute_conversation) | |||
put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) | |||
delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) | |||
put("/scheduled_statuses/:id", ScheduledActivityController, :update) | |||
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) | |||
post("/polls/:id/votes", MastodonAPIController, :poll_vote) | |||
@@ -392,10 +392,10 @@ defmodule Pleroma.Web.Router do | |||
post("/lists/:id/accounts", ListController, :add_to_list) | |||
delete("/lists/:id/accounts", ListController, :remove_from_list) | |||
post("/filters", MastodonAPIController, :create_filter) | |||
get("/filters/:id", MastodonAPIController, :get_filter) | |||
put("/filters/:id", MastodonAPIController, :update_filter) | |||
delete("/filters/:id", MastodonAPIController, :delete_filter) | |||
post("/filters", FilterController, :create) | |||
get("/filters/:id", FilterController, :show) | |||
put("/filters/:id", FilterController, :update) | |||
delete("/filters/:id", FilterController, :delete) | |||
patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar) | |||
patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner) | |||
@@ -419,11 +419,11 @@ defmodule Pleroma.Web.Router do | |||
post("/accounts/:id/mute", MastodonAPIController, :mute) | |||
post("/accounts/:id/unmute", MastodonAPIController, :unmute) | |||
post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request) | |||
post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request) | |||
post("/follow_requests/:id/authorize", FollowRequestController, :authorize) | |||
post("/follow_requests/:id/reject", FollowRequestController, :reject) | |||
post("/domain_blocks", MastodonAPIController, :block_domain) | |||
delete("/domain_blocks", MastodonAPIController, :unblock_domain) | |||
post("/domain_blocks", DomainBlockController, :create) | |||
delete("/domain_blocks", DomainBlockController, :delete) | |||
post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe) | |||
post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe) | |||
@@ -160,6 +160,9 @@ defmodule Pleroma.Mixfile do | |||
{:plug_static_index_html, "~> 1.0.0"}, | |||
{:excoveralls, "~> 0.11.1", only: :test}, | |||
{:flake_id, "~> 0.1.0"}, | |||
{:remote_ip, | |||
git: "https://git.pleroma.social/pleroma/remote_ip.git", | |||
ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, | |||
{:mox, "~> 0.5", only: :test} | |||
] ++ oauth_deps() | |||
end | |||
@@ -48,6 +48,7 @@ | |||
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]}, | |||
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm"}, | |||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, | |||
@@ -88,6 +89,7 @@ | |||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, | |||
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]}, | |||
"sentry": {:hex, :sentry, "7.0.5", "31531b7218d6932dc32c4180a153ec9c2485da019868c666190bca522ce083c8", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "825dc00aaba5a1b7c4202a532b696b595dd3bcb3", [ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"]}, | |||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, | |||
"swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"}, | |||
@@ -0,0 +1,72 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Plugs.RemoteIpTest do | |||
use ExUnit.Case, async: true | |||
use Plug.Test | |||
alias Pleroma.Plugs.RemoteIp | |||
test "disabled" do | |||
Pleroma.Config.put(RemoteIp, enabled: false) | |||
%{remote_ip: remote_ip} = conn(:get, "/") | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == remote_ip | |||
end | |||
test "enabled" do | |||
Pleroma.Config.put(RemoteIp, enabled: true) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
test "custom headers" do | |||
Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
refute conn.remote_ip == {1, 1, 1, 1} | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("cf-connecting-ip", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
test "custom proxies" do | |||
Pleroma.Config.put(RemoteIp, enabled: true) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") | |||
|> RemoteIp.call(nil) | |||
refute conn.remote_ip == {1, 1, 1, 1} | |||
Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"]) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
end |
@@ -0,0 +1,51 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do | |||
use Pleroma.Web.ConnCase, async: true | |||
alias Pleroma.User | |||
import Pleroma.Factory | |||
test "blocking / unblocking a domain", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) | |||
assert %{} = json_response(conn, 200) | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
assert User.blocks?(user, other_user) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) | |||
assert %{} = json_response(conn, 200) | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
refute User.blocks?(user, other_user) | |||
end | |||
test "getting a list of domain blocks", %{conn: conn} do | |||
user = insert(:user) | |||
{:ok, user} = User.block_domain(user, "bad.site") | |||
{:ok, user} = User.block_domain(user, "even.worse.site") | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/domain_blocks") | |||
domain_blocks = json_response(conn, 200) | |||
assert "bad.site" in domain_blocks | |||
assert "even.worse.site" in domain_blocks | |||
end | |||
end |
@@ -0,0 +1,137 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do | |||
use Pleroma.Web.ConnCase, async: true | |||
alias Pleroma.Web.MastodonAPI.FilterView | |||
import Pleroma.Factory | |||
test "creating a filter", %{conn: conn} do | |||
user = insert(:user) | |||
filter = %Pleroma.Filter{ | |||
phrase: "knights", | |||
context: ["home"] | |||
} | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) | |||
assert response = json_response(conn, 200) | |||
assert response["phrase"] == filter.phrase | |||
assert response["context"] == filter.context | |||
assert response["irreversible"] == false | |||
assert response["id"] != nil | |||
assert response["id"] != "" | |||
end | |||
test "fetching a list of filters", %{conn: conn} do | |||
user = insert(:user) | |||
query_one = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 1, | |||
phrase: "knights", | |||
context: ["home"] | |||
} | |||
query_two = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "who", | |||
context: ["home"] | |||
} | |||
{:ok, filter_one} = Pleroma.Filter.create(query_one) | |||
{:ok, filter_two} = Pleroma.Filter.create(query_two) | |||
response = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/filters") | |||
|> json_response(200) | |||
assert response == | |||
render_json( | |||
FilterView, | |||
"filters.json", | |||
filters: [filter_two, filter_one] | |||
) | |||
end | |||
test "get a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, filter} = Pleroma.Filter.create(query) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/filters/#{filter.filter_id}") | |||
assert _response = json_response(conn, 200) | |||
end | |||
test "update a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, _filter} = Pleroma.Filter.create(query) | |||
new = %Pleroma.Filter{ | |||
phrase: "nii", | |||
context: ["home"] | |||
} | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/filters/#{query.filter_id}", %{ | |||
phrase: new.phrase, | |||
context: new.context | |||
}) | |||
assert response = json_response(conn, 200) | |||
assert response["phrase"] == new.phrase | |||
assert response["context"] == new.context | |||
end | |||
test "delete a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, filter} = Pleroma.Filter.create(query) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/filters/#{filter.filter_id}") | |||
assert response = json_response(conn, 200) | |||
assert response == %{} | |||
end | |||
end |
@@ -0,0 +1,81 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
import Pleroma.Factory | |||
describe "locked accounts" do | |||
test "/api/v1/follow_requests works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> get("/api/v1/follow_requests") | |||
assert [relationship] = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
end | |||
test "/api/v1/follow_requests/:id/authorize works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/follow_requests/#{other_user.id}/authorize") | |||
assert relationship = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == true | |||
end | |||
test "/api/v1/follow_requests/:id/reject works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/follow_requests/#{other_user.id}/reject") | |||
assert relationship = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
end | |||
end | |||
end |
@@ -0,0 +1,113 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do | |||
use Pleroma.Web.ConnCase, async: true | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
import Pleroma.Factory | |||
test "shows scheduled activities", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
# min_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
# since_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result | |||
# max_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
end | |||
test "shows a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) | |||
assert scheduled_activity_id == scheduled_activity.id |> to_string() | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/404") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "updates a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
new_scheduled_at = | |||
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ | |||
scheduled_at: new_scheduled_at | |||
}) | |||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) | |||
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "deletes a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{} = json_response(res_conn, 200) | |||
assert nil == Repo.get(ScheduledActivity, scheduled_activity.id) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
end |
@@ -9,7 +9,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do | |||
alias Pleroma.ActivityExpiration | |||
alias Pleroma.Config | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
import Pleroma.Factory | |||
@@ -96,6 +99,27 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do | |||
NaiveDateTime.to_iso8601(expiration.scheduled_at) | |||
end | |||
test "posting an undefined status with an attachment", %{conn: conn} do | |||
user = insert(:user) | |||
file = %Plug.Upload{ | |||
content_type: "image/jpg", | |||
path: Path.absname("test/fixtures/image.jpg"), | |||
filename: "an_image.jpg" | |||
} | |||
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"media_ids" => [to_string(upload.id)] | |||
}) | |||
assert json_response(conn, 200) | |||
end | |||
test "replying to a status", %{conn: conn} do | |||
user = insert(:user) | |||
{:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"}) | |||
@@ -224,6 +248,115 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do | |||
end | |||
end | |||
describe "posting scheduled statuses" do | |||
test "creates a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"status" => "scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200) | |||
assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at) | |||
assert [] == Repo.all(Activity) | |||
end | |||
test "creates a scheduled activity with a media attachment", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
file = %Plug.Upload{ | |||
content_type: "image/jpg", | |||
path: Path.absname("test/fixtures/image.jpg"), | |||
filename: "an_image.jpg" | |||
} | |||
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"media_ids" => [to_string(upload.id)], | |||
"status" => "scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200) | |||
assert %{"type" => "image"} = media_attachment | |||
end | |||
test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now", | |||
%{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = | |||
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"status" => "not scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"content" => "not scheduled"} = json_response(conn, 200) | |||
assert [] == Repo.all(ScheduledActivity) | |||
end | |||
test "returns error when daily user limit is exceeded", %{conn: conn} do | |||
user = insert(:user) | |||
today = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.minutes(6), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
attrs = %{params: %{}, scheduled_at: today} | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) | |||
assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) | |||
end | |||
test "returns error when total user limit is exceeded", %{conn: conn} do | |||
user = insert(:user) | |||
today = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.minutes(6), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
tomorrow = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.hours(36), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
attrs = %{params: %{}, scheduled_at: today} | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) | |||
assert %{"error" => "total limit exceeded"} == json_response(conn, 422) | |||
end | |||
end | |||
describe "posting polls" do | |||
test "posting a poll", %{conn: conn} do | |||
user = insert(:user) | |||
@@ -6,17 +6,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
use Pleroma.Web.ConnCase | |||
alias Ecto.Changeset | |||
alias Pleroma.Activity | |||
alias Pleroma.Config | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Tests.ObanHelpers | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.FilterView | |||
alias Pleroma.Web.OAuth.App | |||
alias Pleroma.Web.OAuth.Token | |||
alias Pleroma.Web.Push | |||
@@ -268,134 +265,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
assert expected == json_response(conn, 200) | |||
end | |||
describe "filters" do | |||
test "creating a filter", %{conn: conn} do | |||
user = insert(:user) | |||
filter = %Pleroma.Filter{ | |||
phrase: "knights", | |||
context: ["home"] | |||
} | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) | |||
assert response = json_response(conn, 200) | |||
assert response["phrase"] == filter.phrase | |||
assert response["context"] == filter.context | |||
assert response["irreversible"] == false | |||
assert response["id"] != nil | |||
assert response["id"] != "" | |||
end | |||
test "fetching a list of filters", %{conn: conn} do | |||
user = insert(:user) | |||
query_one = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 1, | |||
phrase: "knights", | |||
context: ["home"] | |||
} | |||
query_two = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "who", | |||
context: ["home"] | |||
} | |||
{:ok, filter_one} = Pleroma.Filter.create(query_one) | |||
{:ok, filter_two} = Pleroma.Filter.create(query_two) | |||
response = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/filters") | |||
|> json_response(200) | |||
assert response == | |||
render_json( | |||
FilterView, | |||
"filters.json", | |||
filters: [filter_two, filter_one] | |||
) | |||
end | |||
test "get a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, filter} = Pleroma.Filter.create(query) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/filters/#{filter.filter_id}") | |||
assert _response = json_response(conn, 200) | |||
end | |||
test "update a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, _filter} = Pleroma.Filter.create(query) | |||
new = %Pleroma.Filter{ | |||
phrase: "nii", | |||
context: ["home"] | |||
} | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/filters/#{query.filter_id}", %{ | |||
phrase: new.phrase, | |||
context: new.context | |||
}) | |||
assert response = json_response(conn, 200) | |||
assert response["phrase"] == new.phrase | |||
assert response["context"] == new.context | |||
end | |||
test "delete a filter", %{conn: conn} do | |||
user = insert(:user) | |||
query = %Pleroma.Filter{ | |||
user_id: user.id, | |||
filter_id: 2, | |||
phrase: "knight", | |||
context: ["home"] | |||
} | |||
{:ok, filter} = Pleroma.Filter.create(query) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/filters/#{filter.filter_id}") | |||
assert response = json_response(conn, 200) | |||
assert response == %{} | |||
end | |||
end | |||
describe "user timelines" do | |||
test "gets a users statuses", %{conn: conn} do | |||
user_one = insert(:user) | |||
@@ -570,51 +439,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
end | |||
describe "locked accounts" do | |||
test "/api/v1/follow_requests works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> get("/api/v1/follow_requests") | |||
assert [relationship] = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
end | |||
test "/api/v1/follow_requests/:id/authorize works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/follow_requests/#{other_user.id}/authorize") | |||
assert relationship = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == true | |||
end | |||
test "verify_credentials", %{conn: conn} do | |||
user = insert(:user, %{info: %User.Info{default_scope: "private"}}) | |||
@@ -626,28 +450,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) | |||
assert id == to_string(user.id) | |||
end | |||
test "/api/v1/follow_requests/:id/reject works" do | |||
user = insert(:user, %{info: %User.Info{locked: true}}) | |||
other_user = insert(:user) | |||
{:ok, _activity} = ActivityPub.follow(other_user, user) | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/follow_requests/#{other_user.id}/reject") | |||
assert relationship = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
user = User.get_cached_by_id(user.id) | |||
other_user = User.get_cached_by_id(other_user.id) | |||
assert User.following?(other_user, user) == false | |||
end | |||
end | |||
describe "account fetching" do | |||
@@ -1176,46 +978,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
assert [%{"id" => ^other_user_id}] = json_response(conn, 200) | |||
end | |||
test "blocking / unblocking a domain", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) | |||
assert %{} = json_response(conn, 200) | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
assert User.blocks?(user, other_user) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) | |||
assert %{} = json_response(conn, 200) | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
refute User.blocks?(user, other_user) | |||
end | |||
test "getting a list of domain blocks", %{conn: conn} do | |||
user = insert(:user) | |||
{:ok, user} = User.block_domain(user, "bad.site") | |||
{:ok, user} = User.block_domain(user, "even.worse.site") | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/domain_blocks") | |||
domain_blocks = json_response(conn, 200) | |||
assert "bad.site" in domain_blocks | |||
assert "even.worse.site" in domain_blocks | |||
end | |||
test "unimplemented follow_requests, blocks, domain blocks" do | |||
user = insert(:user) | |||
@@ -1810,216 +1572,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
end | |||
end | |||
describe "scheduled activities" do | |||
test "creates a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"status" => "scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200) | |||
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at) | |||
assert [] == Repo.all(Activity) | |||
end | |||
test "creates a scheduled activity with a media attachment", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
file = %Plug.Upload{ | |||
content_type: "image/jpg", | |||
path: Path.absname("test/fixtures/image.jpg"), | |||
filename: "an_image.jpg" | |||
} | |||
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"media_ids" => [to_string(upload.id)], | |||
"status" => "scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200) | |||
assert %{"type" => "image"} = media_attachment | |||
end | |||
test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now", | |||
%{conn: conn} do | |||
user = insert(:user) | |||
scheduled_at = | |||
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{ | |||
"status" => "not scheduled", | |||
"scheduled_at" => scheduled_at | |||
}) | |||
assert %{"content" => "not scheduled"} = json_response(conn, 200) | |||
assert [] == Repo.all(ScheduledActivity) | |||
end | |||
test "returns error when daily user limit is exceeded", %{conn: conn} do | |||
user = insert(:user) | |||
today = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.minutes(6), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
attrs = %{params: %{}, scheduled_at: today} | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) | |||
assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) | |||
end | |||
test "returns error when total user limit is exceeded", %{conn: conn} do | |||
user = insert(:user) | |||
today = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.minutes(6), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
tomorrow = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.hours(36), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
attrs = %{params: %{}, scheduled_at: today} | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, attrs) | |||
{:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) | |||
assert %{"error" => "total limit exceeded"} == json_response(conn, 422) | |||
end | |||
test "shows scheduled activities", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
# min_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
# since_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result | |||
# max_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
end | |||
test "shows a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) | |||
assert scheduled_activity_id == scheduled_activity.id |> to_string() | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/404") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "updates a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
new_scheduled_at = | |||
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ | |||
scheduled_at: new_scheduled_at | |||
}) | |||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) | |||
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "deletes a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{} = json_response(res_conn, 200) | |||
assert nil == Repo.get(ScheduledActivity, scheduled_activity.id) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
end | |||
describe "create account by app" do | |||
test "Account registration via Application", %{conn: conn} do | |||
conn = | |||