wip
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
9032d065e6
103 changed files with 2502 additions and 190 deletions
|
|
@ -199,6 +199,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||
lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||
template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
|
||||
|
||||
|
|
@ -217,6 +218,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
secret: secret,
|
||||
jwt_secret: jwt_secret,
|
||||
signing_salt: signing_salt,
|
||||
lv_signing_salt: lv_signing_salt,
|
||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
|
||||
db_configurable?: db_configurable?,
|
||||
|
|
|
|||
|
|
@ -362,11 +362,9 @@ defmodule Pleroma.Activity do
|
|||
end
|
||||
|
||||
def restrict_deactivated_users(query) do
|
||||
deactivated_users =
|
||||
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||
|> Repo.all()
|
||||
deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||
|
||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
||||
from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
|
||||
end
|
||||
|
||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ defmodule Pleroma.Application do
|
|||
|
||||
adapter = Application.get_env(:tesla, :adapter)
|
||||
|
||||
if match?({Tesla.Adapter.Finch, _}, adapter) do
|
||||
Logger.info("Starting Finch")
|
||||
Finch.start_link(name: MyFinch)
|
||||
end
|
||||
|
||||
if adapter == Tesla.Adapter.Gun do
|
||||
if version = Pleroma.OTPVersion.version() do
|
||||
[major, minor] =
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ defenum(Pleroma.UserRelationship.Type,
|
|||
reblog_mute: 3,
|
||||
notification_mute: 4,
|
||||
inverse_subscription: 5,
|
||||
endorsement: 6
|
||||
suggestion_dismiss: 6,
|
||||
endorsement: 7
|
||||
)
|
||||
|
||||
defenum(Pleroma.FollowingRelationship.State,
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ defmodule Pleroma.Emoji.Loader do
|
|||
pack_file = Path.join(pack_dir, "pack.json")
|
||||
|
||||
if File.exists?(pack_file) do
|
||||
Logger.info("Loading emoji pack from JSON: #{pack_file}")
|
||||
contents = Jason.decode!(File.read!(pack_file))
|
||||
|
||||
contents["files"]
|
||||
|
|
@ -115,6 +116,7 @@ defmodule Pleroma.Emoji.Loader do
|
|||
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
||||
|
||||
if File.exists?(emoji_txt) do
|
||||
Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}")
|
||||
load_from_file(emoji_txt, emoji_groups)
|
||||
else
|
||||
extensions = Config.get([:emoji, :pack_extensions])
|
||||
|
|
|
|||
|
|
@ -338,6 +338,26 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "add_suggestion",
|
||||
"subject" => users
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "remove_suggestion",
|
||||
"subject" => users
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
|
|
|||
|
|
@ -94,7 +94,8 @@ defmodule Pleroma.Pagination do
|
|||
offset: :integer,
|
||||
limit: :integer,
|
||||
skip_extra_order: :boolean,
|
||||
skip_order: :boolean
|
||||
skip_order: :boolean,
|
||||
shuffle: :boolean,
|
||||
}
|
||||
|
||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||
|
|
@ -113,6 +114,10 @@ defmodule Pleroma.Pagination do
|
|||
where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
|
||||
end
|
||||
|
||||
defp restrict(query, :order, %{shuffle: true}, _) do
|
||||
order_by(query, [u], fragment("RANDOM()"))
|
||||
end
|
||||
|
||||
defp restrict(query, :order, %{skip_order: true}, _), do: query
|
||||
|
||||
defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@ defmodule Pleroma.ReverseProxy.Client.Wrapper do
|
|||
|
||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||
defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,10 +12,16 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
[:pleroma, :connection_pool, :provision_failure],
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
[:pleroma, :connection_pool, :client, :add]
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
[:pleroma, :repo, :query]
|
||||
]
|
||||
def attach do
|
||||
:telemetry.attach_many("pleroma-logger", @events, &handle_event/4, [])
|
||||
:telemetry.attach_many(
|
||||
"pleroma-logger",
|
||||
@events,
|
||||
&Pleroma.Telemetry.Logger.handle_event/4,
|
||||
[]
|
||||
)
|
||||
end
|
||||
|
||||
# Passing anonymous functions instead of strings to logger is intentional,
|
||||
|
|
@ -87,4 +93,64 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
end
|
||||
|
||||
def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :repo, :query] = _name,
|
||||
%{query_time: query_time} = measurements,
|
||||
%{source: source} = metadata,
|
||||
config
|
||||
) do
|
||||
logging_config = Pleroma.Config.get([:telemetry, :slow_queries_logging], [])
|
||||
|
||||
if logging_config[:enabled] &&
|
||||
logging_config[:min_duration] &&
|
||||
query_time > logging_config[:min_duration] and
|
||||
(is_nil(logging_config[:exclude_sources]) or
|
||||
source not in logging_config[:exclude_sources]) do
|
||||
log_slow_query(measurements, metadata, config)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp log_slow_query(
|
||||
%{query_time: query_time} = _measurements,
|
||||
%{source: _source, query: query, params: query_params, repo: repo} = _metadata,
|
||||
_config
|
||||
) do
|
||||
sql_explain =
|
||||
with {:ok, %{rows: explain_result_rows}} <-
|
||||
repo.query("EXPLAIN " <> query, query_params, log: false) do
|
||||
Enum.map_join(explain_result_rows, "\n", & &1)
|
||||
end
|
||||
|
||||
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
||||
|
||||
pleroma_stacktrace =
|
||||
Enum.filter(stacktrace, fn
|
||||
{__MODULE__, _, _, _} ->
|
||||
false
|
||||
|
||||
{mod, _, _, _} ->
|
||||
mod
|
||||
|> to_string()
|
||||
|> String.starts_with?("Elixir.Pleroma.")
|
||||
end)
|
||||
|
||||
Logger.warn(fn ->
|
||||
"""
|
||||
Slow query!
|
||||
|
||||
Total time: #{round(query_time / 1_000)} ms
|
||||
|
||||
#{query}
|
||||
|
||||
#{inspect(query_params, limit: :infinity)}
|
||||
|
||||
#{sql_explain}
|
||||
|
||||
#{Exception.format_stacktrace(pleroma_stacktrace)}
|
||||
"""
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ defmodule Pleroma.User do
|
|||
field(:last_active_at, :naive_datetime)
|
||||
field(:disclose_client, :boolean, default: true)
|
||||
field(:pinned_objects, :map, default: %{})
|
||||
field(:is_suggested, :boolean, default: false)
|
||||
field(:last_status_at, :naive_datetime)
|
||||
|
||||
embeds_one(
|
||||
:notification_settings,
|
||||
|
|
@ -1709,6 +1711,22 @@ defmodule Pleroma.User do
|
|||
|
||||
def confirm(%User{} = user), do: {:ok, user}
|
||||
|
||||
def set_suggestion(users, is_suggested) when is_list(users) do
|
||||
Repo.transaction(fn ->
|
||||
Enum.map(users, fn user ->
|
||||
with {:ok, user} <- set_suggestion(user, is_suggested), do: user
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
|
||||
|
||||
def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
|
||||
user
|
||||
|> change(is_suggested: is_suggested)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def update_notification_settings(%User{} = user, settings) do
|
||||
user
|
||||
|> cast(%{notification_settings: settings}, [])
|
||||
|
|
@ -2507,12 +2525,24 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def active_user_count(weeks \\ 4) do
|
||||
active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks)
|
||||
def active_user_count(days \\ 30) do
|
||||
active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
|
||||
|
||||
__MODULE__
|
||||
|> where([u], u.last_active_at >= ^active_after)
|
||||
|> where([u], u.local == true)
|
||||
|> Repo.aggregate(:count)
|
||||
end
|
||||
|
||||
def update_last_status_at(user) do
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> update([u], set: [last_status_at: fragment("NOW()")])
|
||||
|> select([u], u)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ defmodule Pleroma.User.Query do
|
|||
unconfirmed: boolean(),
|
||||
is_admin: boolean(),
|
||||
is_moderator: boolean(),
|
||||
is_suggested: boolean(),
|
||||
is_discoverable: boolean(),
|
||||
super_users: boolean(),
|
||||
invisible: boolean(),
|
||||
internal: boolean(),
|
||||
|
|
@ -167,6 +169,14 @@ defmodule Pleroma.User.Query do
|
|||
where(query, [u], u.is_confirmed == false)
|
||||
end
|
||||
|
||||
defp compose_query({:is_suggested, bool}, query) do
|
||||
where(query, [u], u.is_suggested == ^bool)
|
||||
end
|
||||
|
||||
defp compose_query({:is_discoverable, bool}, query) do
|
||||
where(query, [u], u.is_discoverable == ^bool)
|
||||
end
|
||||
|
||||
defp compose_query({:followers, %User{id: id}}, query) do
|
||||
query
|
||||
|> where([u], u.id != ^id)
|
||||
|
|
|
|||
52
lib/pleroma/user_note.ex
Normal file
52
lib/pleroma/user_note.ex
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.UserNote do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserNote
|
||||
|
||||
schema "user_notes" do
|
||||
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
||||
field(:comment, :string)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(%UserNote{} = user_note, params \\ %{}) do
|
||||
user_note
|
||||
|> cast(params, [:source_id, :target_id, :comment])
|
||||
|> validate_required([:source_id, :target_id])
|
||||
end
|
||||
|
||||
def show(%User{} = source, %User{} = target) do
|
||||
with %UserNote{} = note <-
|
||||
UserNote
|
||||
|> where(source_id: ^source.id, target_id: ^target.id)
|
||||
|> Repo.one() do
|
||||
note.comment
|
||||
else
|
||||
_ -> ""
|
||||
end
|
||||
end
|
||||
|
||||
def create(%User{} = source, %User{} = target, comment) do
|
||||
%UserNote{}
|
||||
|> changeset(%{
|
||||
source_id: source.id,
|
||||
target_id: target.id,
|
||||
comment: comment
|
||||
})
|
||||
|> Repo.insert(
|
||||
on_conflict: {:replace, [:comment]},
|
||||
conflict_target: [:source_id, :target_id]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -81,6 +81,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
def update_last_status_at_if_public(actor, object) do
|
||||
if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
defp increase_replies_count_if_reply(%{
|
||||
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
||||
"type" => "Create"
|
||||
|
|
@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
_ <- increase_replies_count_if_reply(create_data),
|
||||
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
|
||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||
{:ok, _actor} <- update_last_status_at_if_public(actor, activity),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_schedule_poll_notifications(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
|
|
|
|||
|
|
@ -68,12 +68,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
end
|
||||
end
|
||||
|
||||
defp handle_href(href, mediaType) do
|
||||
defp handle_href(href, mediaType, data) do
|
||||
[
|
||||
%{
|
||||
"href" => href,
|
||||
"type" => "Link",
|
||||
"mediaType" => mediaType
|
||||
"mediaType" => mediaType,
|
||||
"width" => data["width"],
|
||||
"height" => data["height"]
|
||||
}
|
||||
]
|
||||
end
|
||||
|
|
@ -81,10 +83,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
defp fix_url(data) do
|
||||
cond do
|
||||
is_binary(data["url"]) ->
|
||||
Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
|
||||
Map.put(data, "url", handle_href(data["url"], data["mediaType"], data))
|
||||
|
||||
is_binary(data["href"]) and data["url"] == nil ->
|
||||
Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
|
||||
Map.put(data, "url", handle_href(data["href"], data["mediaType"], data))
|
||||
|
||||
true ->
|
||||
data
|
||||
|
|
|
|||
|
|
@ -63,18 +63,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
date: date
|
||||
})
|
||||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
result =
|
||||
HTTP.post(
|
||||
inbox,
|
||||
json,
|
||||
[
|
||||
{"Content-Type", "application/activity+json"},
|
||||
{"Date", date},
|
||||
{"signature", signature},
|
||||
{"digest", digest}
|
||||
]
|
||||
) do
|
||||
with {:ok, %{status: code}} = result when code in 200..299 <-
|
||||
HTTP.post(
|
||||
inbox,
|
||||
json,
|
||||
[
|
||||
{"Content-Type", "application/activity+json"},
|
||||
{"Date", date},
|
||||
{"signature", signature},
|
||||
{"digest", digest}
|
||||
]
|
||||
) do
|
||||
if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
|
||||
Instances.set_reachable(inbox)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -199,8 +199,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
||||
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
||||
{:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object)
|
||||
|
||||
if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
|
||||
if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do
|
||||
Object.increase_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> Activity.Queries.by_type()
|
||||
|> Activity.Queries.by_actor(actor)
|
||||
|> Activity.Queries.by_object_id(object)
|
||||
|> where(fragment("data->>'state' = 'pending'"))
|
||||
|> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'"))
|
||||
|> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
|
||||
|> Repo.update_all([])
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
:toggle_activation,
|
||||
:activate,
|
||||
:deactivate,
|
||||
:approve
|
||||
:approve,
|
||||
:suggest,
|
||||
:unsuggest
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -239,6 +241,32 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
render(conn, "index.json", users: updated_users)
|
||||
end
|
||||
|
||||
def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.set_suggestion(users, true)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "add_suggestion"
|
||||
})
|
||||
|
||||
render(conn, "index.json", users: updated_users)
|
||||
end
|
||||
|
||||
def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
{:ok, updated_users} = User.set_suggestion(users, false)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: users,
|
||||
action: "remove_suggestion"
|
||||
})
|
||||
|
||||
render(conn, "index.json", users: updated_users)
|
||||
end
|
||||
|
||||
def index(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
filters = maybe_parse_filters(params[:filters])
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
"tags" => user.tags || [],
|
||||
"is_confirmed" => user.is_confirmed,
|
||||
"is_approved" => user.is_approved,
|
||||
"is_suggested" => user.is_suggested,
|
||||
"url" => user.uri || user.ap_id,
|
||||
"registration_reason" => user.registration_reason,
|
||||
"actor_type" => user.actor_type,
|
||||
|
|
|
|||
|
|
@ -226,6 +226,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
type: :boolean,
|
||||
description: "Receive this account's reblogs in home timeline? Defaults to true.",
|
||||
default: true
|
||||
},
|
||||
notify: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"Receive notifications for all statuses posted by the account? Defaults to false.",
|
||||
default: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -334,8 +340,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
summary: "Endorse",
|
||||
operationId: "AccountController.endorse",
|
||||
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||
description:
|
||||
"Addds the given account to endorsed accounts list.",
|
||||
description: "Addds the given account to endorsed accounts list.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
|
|
@ -357,6 +362,29 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
}
|
||||
end
|
||||
|
||||
def note_operation do
|
||||
%Operation{
|
||||
tags: ["Account actions"],
|
||||
summary: "Set a private note about a user.",
|
||||
operationId: "AccountController.note",
|
||||
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||
requestBody: request_body("Parameters", note_request()),
|
||||
description: "Create a note for the given account.",
|
||||
parameters: [
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(
|
||||
:comment,
|
||||
:query,
|
||||
%Schema{type: :string},
|
||||
"Account note body"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def follow_by_uri_operation do
|
||||
%Operation{
|
||||
tags: ["Account actions"],
|
||||
|
|
@ -714,9 +742,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
"blocked_by" => true,
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => false,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"notifying" => false,
|
||||
"endorsed" => true
|
||||
},
|
||||
%{
|
||||
|
|
@ -728,9 +758,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
"blocked_by" => true,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => true,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"notifying" => false,
|
||||
"endorsed" => false
|
||||
},
|
||||
%{
|
||||
|
|
@ -742,9 +774,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
"blocked_by" => false,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => false,
|
||||
"domain_blocking" => true,
|
||||
"subscribing" => true,
|
||||
"notifying" => true,
|
||||
"endorsed" => false
|
||||
}
|
||||
]
|
||||
|
|
@ -789,6 +823,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
}
|
||||
end
|
||||
|
||||
defp note_request do
|
||||
%Schema{
|
||||
title: "AccountNoteRequest",
|
||||
description: "POST body for adding a note for an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
comment: %Schema{
|
||||
type: :string,
|
||||
description: "Account note body"
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"comment" => "Example note"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_lists do
|
||||
%Schema{
|
||||
title: "ArrayOfLists",
|
||||
|
|
|
|||
|
|
@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
|
|||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
description: "POST body for deleting multiple users",
|
||||
description: "POST body for approving multiple users",
|
||||
type: :object,
|
||||
properties: %{
|
||||
nicknames: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string}
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Response", "application/json", %Schema{
|
||||
type: :object,
|
||||
properties: %{user: %Schema{type: :array, items: user()}}
|
||||
}),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def suggest_operation do
|
||||
%Operation{
|
||||
tags: ["User administration"],
|
||||
summary: "Suggest multiple users",
|
||||
operationId: "AdminAPI.UserController.suggest",
|
||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||
parameters: admin_api_params(),
|
||||
requestBody:
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
description: "POST body for adding multiple suggested users",
|
||||
type: :object,
|
||||
properties: %{
|
||||
nicknames: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string}
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Response", "application/json", %Schema{
|
||||
type: :object,
|
||||
properties: %{user: %Schema{type: :array, items: user()}}
|
||||
}),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unsuggest_operation do
|
||||
%Operation{
|
||||
tags: ["User administration"],
|
||||
summary: "Unsuggest multiple users",
|
||||
operationId: "AdminAPI.UserController.unsuggest",
|
||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||
parameters: admin_api_params(),
|
||||
requestBody:
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
description: "POST body for removing multiple suggested users",
|
||||
type: :object,
|
||||
properties: %{
|
||||
nicknames: %Schema{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
|||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Helpers
|
||||
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
|
|
@ -22,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
|||
operationId: "AppController.create",
|
||||
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("App", "application/json", create_response()),
|
||||
200 => Operation.response("App", "application/json", App),
|
||||
422 =>
|
||||
Operation.response(
|
||||
"Unprocessable Entity",
|
||||
|
|
@ -119,30 +120,4 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
|||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_response do
|
||||
%Schema{
|
||||
title: "AppCreateResponse",
|
||||
description: "Response schema for an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
name: %Schema{type: :string},
|
||||
client_id: %Schema{type: :string},
|
||||
client_secret: %Schema{type: :string},
|
||||
redirect_uri: %Schema{type: :string},
|
||||
vapid_key: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"name" => "My App",
|
||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||
"vapid_key" =>
|
||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
41
lib/pleroma/web/api_spec/operations/directory_operation.ex
Normal file
41
lib/pleroma/web/api_spec/operations/directory_operation.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.DirectoryOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def index_operation do
|
||||
%Operation{
|
||||
tags: ["Directory"],
|
||||
summary: "Profile directory",
|
||||
operationId: "DirectoryController.index",
|
||||
parameters:
|
||||
[
|
||||
Operation.parameter(
|
||||
:order,
|
||||
:query,
|
||||
:string,
|
||||
"Order by recent activity or account creation",
|
||||
required: nil
|
||||
),
|
||||
Operation.parameter(:local, :query, BooleanLike, "Include local users only")
|
||||
] ++ pagination_params(),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -69,7 +69,16 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
|||
summary: "Endorsements",
|
||||
description: "Returns endorsed accounts",
|
||||
operationId: "PleromaAPI.AccountController.endorsements",
|
||||
parameters: [id_param() | pagination_params()],
|
||||
parameters:
|
||||
[
|
||||
Operation.parameter(
|
||||
:shuffle,
|
||||
:query,
|
||||
:boolean,
|
||||
"Show endorsed accounts in random order"
|
||||
),
|
||||
id_param()
|
||||
] ++ pagination_params(),
|
||||
security: [%{"oAuth" => ["read:account"]}],
|
||||
responses: %{
|
||||
200 =>
|
||||
|
|
|
|||
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
@spec index_operation() :: Operation.t()
|
||||
def index_operation do
|
||||
%Operation{
|
||||
tags: ["Applications"],
|
||||
summary: "List applications",
|
||||
description: "List the OAuth applications for the current user",
|
||||
operationId: "AppController.index",
|
||||
responses: %{
|
||||
200 => Operation.response("Array of App", "application/json", array_of_apps())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_apps do
|
||||
%Schema{type: :array, items: App, example: [App.schema().example]}
|
||||
end
|
||||
end
|
||||
|
|
@ -191,6 +191,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Password")
|
||||
],
|
||||
requestBody: request_body("Parameters", delete_account_request(), required: false),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
|
|
@ -237,4 +238,48 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||
}
|
||||
end
|
||||
|
||||
def remote_interaction_operation do
|
||||
%Operation{
|
||||
tags: ["Accounts"],
|
||||
summary: "Remote interaction",
|
||||
operationId: "UtilController.remote_interaction",
|
||||
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp remote_interaction_request do
|
||||
%Schema{
|
||||
title: "RemoteInteractionRequest",
|
||||
description: "POST body for remote interaction",
|
||||
type: :object,
|
||||
required: [:ap_id, :profile],
|
||||
properties: %{
|
||||
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
||||
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp delete_account_request do
|
||||
%Schema{
|
||||
title: "AccountDeleteRequest",
|
||||
description: "POST body for deleting one's own account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
password: %Schema{
|
||||
type: :string,
|
||||
description: "The user's own password for confirmation.",
|
||||
format: :password
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"password" => "prettyp0ony1313"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -194,9 +194,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
"subscribing" => false,
|
||||
"notifying" => false
|
||||
},
|
||||
"settings_store" => %{
|
||||
"pleroma-fe" => %{}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
|||
id: FlakeID,
|
||||
muting: %Schema{type: :boolean},
|
||||
muting_notifications: %Schema{type: :boolean},
|
||||
note: %Schema{type: :string},
|
||||
requested: %Schema{type: :boolean},
|
||||
showing_reblogs: %Schema{type: :boolean},
|
||||
subscribing: %Schema{type: :boolean}
|
||||
subscribing: %Schema{type: :boolean},
|
||||
notifying: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"blocked_by" => false,
|
||||
|
|
@ -36,9 +38,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
|||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
"subscribing" => false,
|
||||
"notifying" => false
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
|
|||
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.App do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "App",
|
||||
description: "Response schema for an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
name: %Schema{type: :string},
|
||||
client_id: %Schema{type: :string},
|
||||
client_secret: %Schema{type: :string},
|
||||
redirect_uri: %Schema{type: :string},
|
||||
vapid_key: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"name" => "My App",
|
||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||
"vapid_key" =>
|
||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
@ -282,9 +282,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"note" => "",
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
"subscribing" => false,
|
||||
"notifying" => false
|
||||
},
|
||||
"skip_thread_containment" => false,
|
||||
"tags" => []
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
alias Pleroma.Config
|
||||
|
||||
socket("/socket", Pleroma.Web.UserSocket)
|
||||
socket("/live", Phoenix.LiveView.Socket)
|
||||
|
||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||
|
||||
|
|
|
|||
14
lib/pleroma/web/manifest_controller.ex
Normal file
14
lib/pleroma/web/manifest_controller.ex
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ManifestController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
plug(:skip_auth when action == :show)
|
||||
|
||||
@doc "GET /manifest.json"
|
||||
def show(conn, _params) do
|
||||
render(conn, "manifest.json")
|
||||
end
|
||||
end
|
||||
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserNote
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
|
|
@ -55,7 +56,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"]} when action in [:update_credentials, :endorse, :unendorse]
|
||||
%{scopes: ["write:accounts"]}
|
||||
when action in [:update_credentials, :note, :endorse, :unendorse]
|
||||
)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
||||
|
|
@ -82,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||
|
||||
@relationship_actions [:follow, :unfollow]
|
||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock endorse unendorse)a
|
||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock endorse unendorse endorse unendorse)a
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
|
|
@ -438,6 +440,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/note"
|
||||
def note(
|
||||
%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn,
|
||||
_params
|
||||
) do
|
||||
with {:ok, _user_note} <- UserNote.create(noter, target, comment) do
|
||||
render(conn, "relationship.json", user: noter, target: target)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
|
||||
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
|
@ -26,11 +28,13 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
@doc "POST /api/v1/apps"
|
||||
def create(%{body_params: params} = conn, _params) do
|
||||
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||
user_id = get_user_id(conn)
|
||||
|
||||
app_attrs =
|
||||
params
|
||||
|> Map.take([:client_name, :redirect_uris, :website])
|
||||
|> Map.put(:scopes, scopes)
|
||||
|> Maps.put_if_present(:user_id, user_id)
|
||||
|
||||
with cs <- App.register_changeset(%App{}, app_attrs),
|
||||
{:ok, app} <- Repo.insert(cs) do
|
||||
|
|
@ -38,6 +42,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
end
|
||||
end
|
||||
|
||||
defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
|
||||
defp get_user_id(_conn), do: nil
|
||||
|
||||
@doc """
|
||||
GET /api/v1/apps/verify_credentials
|
||||
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.DirectoryController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Ecto.Query
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
require Logger
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(:skip_auth when action == "index")
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation
|
||||
|
||||
@doc "GET /api/v1/directory"
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
with true <- Pleroma.Config.get([:instance, :profile_directory]) do
|
||||
limit = Map.get(params, :limit, 20) |> min(80)
|
||||
|
||||
users =
|
||||
User.Query.build(%{is_discoverable: true, invisible: false, limit: limit})
|
||||
|> order_by_creation_date(params)
|
||||
|> exclude_remote(params)
|
||||
|> exclude_user(user)
|
||||
|> exclude_relationships(user, [:block, :mute])
|
||||
|> Pagination.fetch_paginated(params, :offset)
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", for: user, users: users, as: :user)
|
||||
else
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
defp order_by_creation_date(query, %{order: "new"}) do
|
||||
query
|
||||
end
|
||||
|
||||
defp order_by_creation_date(query, _params) do
|
||||
query
|
||||
|> order_by([u], desc_nulls_last: u.last_status_at)
|
||||
end
|
||||
|
||||
defp exclude_remote(query, %{local: true}) do
|
||||
where(query, [u], u.local == true)
|
||||
end
|
||||
|
||||
defp exclude_remote(query, _params) do
|
||||
query
|
||||
end
|
||||
|
||||
defp exclude_user(query, %User{id: user_id}) do
|
||||
where(query, [u], u.id != ^user_id)
|
||||
end
|
||||
|
||||
defp exclude_user(query, _user) do
|
||||
query
|
||||
end
|
||||
|
||||
defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
|
||||
query
|
||||
|> join(:left, [u], r in UserRelationship,
|
||||
as: :user_relationships,
|
||||
on:
|
||||
r.target_id == u.id and r.source_id == ^user_id and
|
||||
r.relationship_type in ^relationship_types
|
||||
)
|
||||
|> where([user_relationships: r], is_nil(r.target_id))
|
||||
end
|
||||
|
||||
defp exclude_relationships(query, _user, _relationship_types) do
|
||||
query
|
||||
end
|
||||
end
|
||||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||
|
||||
require Logger
|
||||
|
||||
@search_limit 40
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
||||
|
|
@ -77,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||
[
|
||||
resolve: params[:resolve],
|
||||
following: params[:following],
|
||||
limit: params[:limit],
|
||||
limit: min(params[:limit], @search_limit),
|
||||
offset: params[:offset],
|
||||
type: params[:type],
|
||||
author: get_author(params),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@
|
|||
|
||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||
use Pleroma.Web, :controller
|
||||
import Ecto.Query
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
require Logger
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
|
||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
|
||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
|
|
@ -26,7 +31,90 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
|||
}
|
||||
end
|
||||
|
||||
def index2_operation do
|
||||
%OpenApiSpex.Operation{
|
||||
tags: ["Suggestions"],
|
||||
summary: "Follow suggestions",
|
||||
operationId: "SuggestionController.index2",
|
||||
responses: %{
|
||||
200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def dismiss_operation do
|
||||
%OpenApiSpex.Operation{
|
||||
tags: ["Suggestions"],
|
||||
summary: "Remove a suggestion",
|
||||
operationId: "SuggestionController.dismiss",
|
||||
parameters: [
|
||||
OpenApiSpex.Operation.parameter(
|
||||
:account_id,
|
||||
:path,
|
||||
%OpenApiSpex.Schema{type: :string},
|
||||
"Account to dismiss",
|
||||
required: true
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/suggestions"
|
||||
def index(conn, params),
|
||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||
|
||||
@doc "GET /api/v2/suggestions"
|
||||
def index2(%{assigns: %{user: user}} = conn, params) do
|
||||
limit = Map.get(params, :limit, 40) |> min(80)
|
||||
|
||||
users =
|
||||
%{is_suggested: true, invisible: false, limit: limit}
|
||||
|> User.Query.build()
|
||||
|> exclude_user(user)
|
||||
|> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
|
||||
|> exclude_following(user)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
render(conn, "index.json", %{
|
||||
users: users,
|
||||
source: :staff,
|
||||
for: user,
|
||||
skip_visibility_check: true
|
||||
})
|
||||
end
|
||||
|
||||
defp exclude_user(query, %User{id: user_id}) do
|
||||
where(query, [u], u.id != ^user_id)
|
||||
end
|
||||
|
||||
defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
|
||||
query
|
||||
|> join(:left, [u], r in UserRelationship,
|
||||
as: :user_relationships,
|
||||
on:
|
||||
r.target_id == u.id and r.source_id == ^user_id and
|
||||
r.relationship_type in ^relationship_types
|
||||
)
|
||||
|> where([user_relationships: r], is_nil(r.target_id))
|
||||
end
|
||||
|
||||
defp exclude_following(query, %User{id: user_id}) do
|
||||
query
|
||||
|> join(:left, [u], r in FollowingRelationship,
|
||||
as: :following_relationships,
|
||||
on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
|
||||
)
|
||||
|> where([following_relationships: r], is_nil(r.following_id))
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/suggestions/:account_id"
|
||||
def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
|
||||
with %User{} = target <- User.get_cached_by_id(user_id),
|
||||
{:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
with {:ok, follower, _followed, _} <- result do
|
||||
options = cast_params(params)
|
||||
set_reblogs_visibility(options[:reblogs], result)
|
||||
set_subscription(options[:notify], result)
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
|
|
@ -36,6 +37,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
CommonAPI.show_reblogs(follower, followed)
|
||||
end
|
||||
|
||||
defp set_subscription(true, {:ok, follower, followed, _}) do
|
||||
User.subscribe(follower, followed)
|
||||
end
|
||||
|
||||
defp set_subscription(false, {:ok, follower, followed, _}) do
|
||||
User.unsubscribe(follower, followed)
|
||||
end
|
||||
|
||||
defp set_subscription(_, _), do: {:ok, nil}
|
||||
|
||||
@spec get_followers(User.t(), map()) :: list(User.t())
|
||||
def get_followers(user, params \\ %{}) do
|
||||
user
|
||||
|
|
@ -73,7 +84,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
exclude_visibilities: {:array, :string},
|
||||
reblogs: :boolean,
|
||||
with_muted: :boolean,
|
||||
account_ap_id: :string
|
||||
account_ap_id: :string,
|
||||
notify: :boolean
|
||||
}
|
||||
|
||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserNote
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
|
@ -101,6 +102,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
User.following?(target, reading_user)
|
||||
end
|
||||
|
||||
subscribing =
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:inverse_subscription,
|
||||
target,
|
||||
reading_user,
|
||||
&User.subscribed_to?(&2, &1)
|
||||
)
|
||||
|
||||
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
|
||||
%{
|
||||
id: to_string(target.id),
|
||||
|
|
@ -138,14 +148,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
target,
|
||||
&User.muted_notifications?(&1, &2)
|
||||
),
|
||||
subscribing:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:inverse_subscription,
|
||||
target,
|
||||
reading_user,
|
||||
&User.subscribed_to?(&2, &1)
|
||||
),
|
||||
subscribing: subscribing,
|
||||
notifying: subscribing,
|
||||
requested: follow_state == :follow_pending,
|
||||
domain_blocking: User.blocks_domain?(reading_user, target),
|
||||
showing_reblogs:
|
||||
|
|
@ -156,6 +160,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
target,
|
||||
&User.muting_reblogs?(&1, &2)
|
||||
),
|
||||
note:
|
||||
UserNote.show(
|
||||
reading_user,
|
||||
target
|
||||
),
|
||||
endorsed:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
|
|
@ -163,7 +172,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
target,
|
||||
reading_user,
|
||||
&User.endorses?(&2, &1)
|
||||
),
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -268,6 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
actor_type: user.actor_type
|
||||
}
|
||||
},
|
||||
last_status_at: user.last_status_at,
|
||||
|
||||
# Pleroma extensions
|
||||
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
||||
|
|
@ -276,6 +286,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
ap_id: user.ap_id,
|
||||
also_known_as: user.also_known_as,
|
||||
is_confirmed: user.is_confirmed,
|
||||
is_suggested: user.is_suggested,
|
||||
tags: user.tags,
|
||||
hide_followers_count: user.hide_followers_count,
|
||||
hide_follows_count: user.hide_follows_count,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
features: features(),
|
||||
federation: federation(),
|
||||
fields_limits: fields_limits(),
|
||||
post_formats: Config.get([:instance, :allowed_post_formats])
|
||||
post_formats: Config.get([:instance, :allowed_post_formats]),
|
||||
privileged_staff: Config.get([:instance, :privileged_staff])
|
||||
},
|
||||
stats: %{mau: Pleroma.User.active_user_count()},
|
||||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||
|
|
@ -59,6 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
"mastodon_api",
|
||||
"mastodon_api_streaming",
|
||||
"polls",
|
||||
"v2_suggestions",
|
||||
"pleroma_explicit_addressing",
|
||||
"shareable_emoji_packs",
|
||||
"multifetch",
|
||||
|
|
@ -83,7 +85,13 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
"safe_dm_mentions"
|
||||
end,
|
||||
"pleroma_emoji_reactions",
|
||||
"pleroma_chat_messages"
|
||||
"pleroma_chat_messages",
|
||||
if Config.get([:instance, :show_reactions]) do
|
||||
"exposable_reactions"
|
||||
end,
|
||||
if Config.get([:instance, :profile_directory]) do
|
||||
"profile_directory"
|
||||
end
|
||||
]
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
|
|
|||
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SuggestionView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
@source_types [:staff, :global, :past_interactions]
|
||||
|
||||
def render("index.json", %{users: users} = opts) do
|
||||
Enum.map(users, fn user ->
|
||||
opts =
|
||||
opts
|
||||
|> Map.put(:user, user)
|
||||
|> Map.delete(:users)
|
||||
|
||||
render("show.json", opts)
|
||||
end)
|
||||
end
|
||||
|
||||
def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
|
||||
%{
|
||||
source: source,
|
||||
account: AccountView.render("show.json", opts)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -35,7 +35,9 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
|
|||
openRegistrations: Config.get([:instance, :registrations_open]),
|
||||
usage: %{
|
||||
users: %{
|
||||
total: Map.get(stats, :user_count, 0)
|
||||
total: Map.get(stats, :user_count, 0),
|
||||
activeMonth: Pleroma.User.active_user_count(30),
|
||||
activeHalfyear: Pleroma.User.active_user_count(180)
|
||||
},
|
||||
localPosts: Map.get(stats, :status_count, 0)
|
||||
},
|
||||
|
|
@ -67,7 +69,8 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
|
|||
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
|
||||
features: features,
|
||||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
|
||||
privilegedStaff: Config.get([:instance, :privileged_staff])
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
|
|
@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
field(:client_secret, :string)
|
||||
field(:trusted, :boolean, default: false)
|
||||
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
|
||||
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
||||
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
||||
|
||||
|
|
@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
|
||||
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
||||
def changeset(struct, params) do
|
||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
|
||||
end
|
||||
|
||||
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
||||
|
|
@ -129,6 +132,12 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
{:ok, Repo.all(query), count}
|
||||
end
|
||||
|
||||
@spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()}
|
||||
def get_user_apps(%User{id: user_id}) do
|
||||
from(a in __MODULE__, where: a.user_id == ^user_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||
def destroy(id) do
|
||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
users =
|
||||
user
|
||||
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
|> fetch_paginated_endorsements(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(users)
|
||||
|
|
@ -118,6 +118,16 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
)
|
||||
end
|
||||
|
||||
defp fetch_paginated_endorsements(user, %{shuffle: true} = params) do
|
||||
user
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :shuffle, true))
|
||||
end
|
||||
|
||||
defp fetch_paginated_endorsements(user, params) do
|
||||
user
|
||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
||||
|
|
|
|||
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.AppController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index])
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation
|
||||
|
||||
@doc "GET /api/v1/pleroma/apps"
|
||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||
with apps <- App.get_user_apps(user) do
|
||||
render(conn, "index.json", %{apps: apps})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -151,7 +151,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
index_query(user, params)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
render(conn, "index.json", chats: chats)
|
||||
conn
|
||||
|> add_link_headers(chats)
|
||||
|> render("index.json", chats: chats)
|
||||
end
|
||||
|
||||
defp index_query(%{id: user_id} = user, params) do
|
||||
|
|
|
|||
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.AppView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
def render("index.json", %{apps: apps}) do
|
||||
render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json")
|
||||
end
|
||||
end
|
||||
36
lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex
Normal file
36
lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do
|
||||
@moduledoc """
|
||||
Ensures staff are privileged enough to do certain tasks.
|
||||
"""
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
|
||||
|
||||
def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _) do
|
||||
if Config.get!([:instance, :privileged_staff]) do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> render_error(:forbidden, "User is not an admin.")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
|> render_error(:forbidden, "User is not a staff member.")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Router do
|
||||
use Pleroma.Web, :router
|
||||
import Phoenix.LiveDashboard.Router
|
||||
|
||||
pipeline :accepts_html do
|
||||
plug(:accepts, ["html"])
|
||||
|
|
@ -100,6 +101,10 @@ defmodule Pleroma.Web.Router do
|
|||
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
||||
end
|
||||
|
||||
pipeline :require_privileged_staff do
|
||||
plug(Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug)
|
||||
end
|
||||
|
||||
pipeline :require_admin do
|
||||
plug(Pleroma.Web.Plugs.UserIsAdminPlug)
|
||||
end
|
||||
|
|
@ -150,6 +155,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/emoji", UtilController, :emoji)
|
||||
get("/captcha", UtilController, :captcha)
|
||||
get("/healthcheck", UtilController, :healthcheck)
|
||||
post("/remote_interaction", UtilController, :remote_interaction)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web do
|
||||
|
|
@ -157,12 +163,11 @@ defmodule Pleroma.Web.Router do
|
|||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||
end
|
||||
|
||||
# AdminAPI: only admins can perform these actions
|
||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through([:admin_api, :require_admin])
|
||||
|
||||
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
||||
put("/users/tag", AdminAPIController, :tag_users)
|
||||
delete("/users/tag", AdminAPIController, :untag_users)
|
||||
|
||||
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
||||
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
||||
|
|
@ -185,35 +190,19 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/users/follow", UserController, :follow)
|
||||
post("/users/unfollow", UserController, :unfollow)
|
||||
delete("/users", UserController, :delete)
|
||||
post("/users", UserController, :create)
|
||||
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
||||
patch("/users/activate", UserController, :activate)
|
||||
patch("/users/deactivate", UserController, :deactivate)
|
||||
patch("/users/approve", UserController, :approve)
|
||||
|
||||
patch("/users/suggest", UserController, :suggest)
|
||||
patch("/users/unsuggest", UserController, :unsuggest)
|
||||
|
||||
get("/relay", RelayController, :index)
|
||||
post("/relay", RelayController, :follow)
|
||||
delete("/relay", RelayController, :unfollow)
|
||||
|
||||
post("/users/invite_token", InviteController, :create)
|
||||
get("/users/invites", InviteController, :index)
|
||||
post("/users/revoke_invite", InviteController, :revoke)
|
||||
post("/users/email_invite", InviteController, :email)
|
||||
|
||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||
|
||||
get("/users", UserController, :index)
|
||||
get("/users/:nickname", UserController, :show)
|
||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||
|
||||
get("/instances/:instance/statuses", InstanceController, :list_statuses)
|
||||
delete("/instances/:instance", InstanceController, :delete)
|
||||
|
||||
get("/instance_document/:name", InstanceDocumentController, :show)
|
||||
patch("/instance_document/:name", InstanceDocumentController, :update)
|
||||
delete("/instance_document/:name", InstanceDocumentController, :delete)
|
||||
|
|
@ -221,28 +210,12 @@ defmodule Pleroma.Web.Router do
|
|||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||
|
||||
get("/reports", ReportController, :index)
|
||||
get("/reports/:id", ReportController, :show)
|
||||
patch("/reports", ReportController, :update)
|
||||
post("/reports/:id/notes", ReportController, :notes_create)
|
||||
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
||||
|
||||
get("/statuses/:id", StatusController, :show)
|
||||
put("/statuses/:id", StatusController, :update)
|
||||
delete("/statuses/:id", StatusController, :delete)
|
||||
get("/statuses", StatusController, :index)
|
||||
|
||||
get("/config", ConfigController, :show)
|
||||
post("/config", ConfigController, :update)
|
||||
get("/config/descriptions", ConfigController, :descriptions)
|
||||
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||
get("/restart", AdminAPIController, :restart)
|
||||
|
||||
get("/moderation_log", AdminAPIController, :list_log)
|
||||
|
||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||
get("/stats", AdminAPIController, :stats)
|
||||
|
||||
get("/oauth_app", OAuthAppController, :index)
|
||||
post("/oauth_app", OAuthAppController, :create)
|
||||
patch("/oauth_app/:id", OAuthAppController, :update)
|
||||
|
|
@ -252,19 +225,74 @@ defmodule Pleroma.Web.Router do
|
|||
post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
|
||||
post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
|
||||
|
||||
get("/chats/:id", ChatController, :show)
|
||||
get("/chats/:id/messages", ChatController, :messages)
|
||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||
|
||||
get("/frontends", FrontendController, :index)
|
||||
post("/frontends/install", FrontendController, :install)
|
||||
|
||||
post("/backups", AdminAPIController, :create_backup)
|
||||
end
|
||||
|
||||
# AdminAPI: admins and mods (staff) can perform these actions (if enabled by config)
|
||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through([:admin_api, :require_privileged_staff])
|
||||
|
||||
delete("/users", UserController, :delete)
|
||||
|
||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||
|
||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||
|
||||
get("/statuses", StatusController, :index)
|
||||
|
||||
get("/chats/:id", ChatController, :show)
|
||||
get("/chats/:id/messages", ChatController, :messages)
|
||||
end
|
||||
|
||||
# AdminAPI: admins and mods (staff) can perform these actions
|
||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through(:admin_api)
|
||||
|
||||
put("/users/tag", AdminAPIController, :tag_users)
|
||||
delete("/users/tag", AdminAPIController, :untag_users)
|
||||
|
||||
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
||||
patch("/users/activate", UserController, :activate)
|
||||
patch("/users/deactivate", UserController, :deactivate)
|
||||
patch("/users/approve", UserController, :approve)
|
||||
|
||||
post("/users/invite_token", InviteController, :create)
|
||||
get("/users/invites", InviteController, :index)
|
||||
post("/users/revoke_invite", InviteController, :revoke)
|
||||
post("/users/email_invite", InviteController, :email)
|
||||
|
||||
get("/users", UserController, :index)
|
||||
get("/users/:nickname", UserController, :show)
|
||||
|
||||
get("/instances/:instance/statuses", InstanceController, :list_statuses)
|
||||
delete("/instances/:instance", InstanceController, :delete)
|
||||
|
||||
get("/reports", ReportController, :index)
|
||||
get("/reports/:id", ReportController, :show)
|
||||
patch("/reports", ReportController, :update)
|
||||
post("/reports/:id/notes", ReportController, :notes_create)
|
||||
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
||||
|
||||
get("/statuses/:id", StatusController, :show)
|
||||
put("/statuses/:id", StatusController, :update)
|
||||
delete("/statuses/:id", StatusController, :delete)
|
||||
|
||||
get("/moderation_log", AdminAPIController, :list_log)
|
||||
|
||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||
get("/stats", AdminAPIController, :stats)
|
||||
|
||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||
scope "/pack" do
|
||||
pipe_through([:admin_api, :require_admin])
|
||||
pipe_through(:admin_api)
|
||||
|
||||
post("/", EmojiPackController, :create)
|
||||
patch("/", EmojiPackController, :update)
|
||||
|
|
@ -279,7 +307,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
# Modifying packs
|
||||
scope "/packs" do
|
||||
pipe_through([:admin_api, :require_admin])
|
||||
pipe_through(:admin_api)
|
||||
|
||||
get("/import", EmojiPackController, :import_from_filesystem)
|
||||
get("/remote", EmojiPackController, :remote)
|
||||
|
|
@ -367,6 +395,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:api)
|
||||
|
||||
get("/apps", AppController, :index)
|
||||
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
||||
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
||||
end
|
||||
|
|
@ -457,6 +486,7 @@ defmodule Pleroma.Web.Router do
|
|||
post("/accounts/:id/unblock", AccountController, :unblock)
|
||||
post("/accounts/:id/mute", AccountController, :mute)
|
||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||
post("/accounts/:id/note", AccountController, :note)
|
||||
post("/accounts/:id/pin", AccountController, :endorse)
|
||||
post("/accounts/:id/unpin", AccountController, :unendorse)
|
||||
|
||||
|
|
@ -538,6 +568,7 @@ defmodule Pleroma.Web.Router do
|
|||
delete("/push/subscription", SubscriptionController, :delete)
|
||||
|
||||
get("/suggestions", SuggestionController, :index)
|
||||
delete("/suggestions/:account_id", SuggestionController, :dismiss)
|
||||
|
||||
get("/timelines/home", TimelineController, :home)
|
||||
get("/timelines/direct", TimelineController, :direct)
|
||||
|
|
@ -582,6 +613,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
||||
|
||||
get("/polls/:id", PollController, :show)
|
||||
|
||||
get("/directory", DirectoryController, :index)
|
||||
end
|
||||
|
||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||
|
|
@ -589,6 +622,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/search", SearchController, :search2)
|
||||
|
||||
post("/media", MediaController, :create2)
|
||||
|
||||
get("/suggestions", SuggestionController, :index2)
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web do
|
||||
|
|
@ -630,6 +665,11 @@ defmodule Pleroma.Web.Router do
|
|||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||
|
||||
# Notice compatibility routes for other frontends
|
||||
get("/@:nickname/:id", OStatus.OStatusController, :notice)
|
||||
get("/@:nickname/posts/:id", OStatus.OStatusController, :notice)
|
||||
get("/:nickname/status/:id", OStatus.OStatusController, :notice)
|
||||
|
||||
# Mastodon compatibility routes
|
||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||
|
|
@ -739,6 +779,18 @@ defmodule Pleroma.Web.Router do
|
|||
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:api)
|
||||
|
||||
get("/manifest.json", ManifestController, :show)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:pleroma_html)
|
||||
|
||||
post("/auth/password", TwitterAPI.PasswordController, :request)
|
||||
end
|
||||
|
||||
scope "/proxy/", Pleroma.Web do
|
||||
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
|
||||
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
|
||||
|
|
@ -754,6 +806,11 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
end
|
||||
|
||||
scope "/" do
|
||||
pipe_through([:pleroma_html, :authenticate, :require_admin])
|
||||
live_dashboard("/phoenix/live_dashboard")
|
||||
end
|
||||
|
||||
# Test-only routes needed to test action dispatching and plug chain execution
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
@test_actions [
|
||||
|
|
|
|||
|
|
@ -167,6 +167,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
|
||||
do: assign(conn, :username_or_id, user_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
|||
|
||||
require Logger
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
alias Pleroma.PasswordResetToken
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||
|
||||
@doc "POST /auth/password"
|
||||
def request(conn, params) do
|
||||
nickname_or_email = params["email"] || params["nickname"]
|
||||
|
||||
TwitterAPI.password_reset(nickname_or_email)
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
||||
def reset(conn, %{"token" => token}) do
|
||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||
|
|
|
|||
|
|
@ -62,6 +62,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
end
|
||||
|
||||
def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do
|
||||
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
||||
conn
|
||||
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
||||
else
|
||||
_e -> json(conn, %{error: "Couldn't find user"})
|
||||
end
|
||||
end
|
||||
|
||||
def frontend_configurations(conn, _params) do
|
||||
render(conn, "frontend_configurations.json")
|
||||
end
|
||||
|
|
@ -123,8 +132,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
end
|
||||
|
||||
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||
password = params[:password] || ""
|
||||
def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do
|
||||
# This endpoint can accept a query param or JSON body for backwards-compatibility.
|
||||
# Submitting a JSON body is recommended, so passwords don't end up in server logs.
|
||||
password = body_params[:password] || params[:password] || ""
|
||||
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
|
|
|
|||
28
lib/pleroma/web/views/manifest_view.ex
Normal file
28
lib/pleroma/web/views/manifest_view.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ManifestView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
def render("manifest.json", _params) do
|
||||
%{
|
||||
name: Config.get([:instance, :name]),
|
||||
description: Config.get([:instance, :description]),
|
||||
icons: Config.get([:manifest, :icons]),
|
||||
theme_color: Config.get([:manifest, :theme_color]),
|
||||
background_color: Config.get([:manifest, :background_color]),
|
||||
display: "standalone",
|
||||
scope: Endpoint.url(),
|
||||
start_url: "/",
|
||||
categories: [
|
||||
"social"
|
||||
],
|
||||
serviceworker: %{
|
||||
src: "/sw.js"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue