logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

account_controller.ex (22810B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MastodonAPI.AccountController do
  5. use Pleroma.Web, :controller
  6. import Pleroma.Web.ControllerHelper,
  7. only: [
  8. add_link_headers: 2,
  9. assign_account_by_id: 2,
  10. embed_relationships?: 1,
  11. json_response: 3
  12. ]
  13. alias Pleroma.Maps
  14. alias Pleroma.User
  15. alias Pleroma.UserNote
  16. alias Pleroma.Web.ActivityPub.ActivityPub
  17. alias Pleroma.Web.ActivityPub.Builder
  18. alias Pleroma.Web.ActivityPub.Pipeline
  19. alias Pleroma.Web.CommonAPI
  20. alias Pleroma.Web.MastodonAPI.ListView
  21. alias Pleroma.Web.MastodonAPI.MastodonAPI
  22. alias Pleroma.Web.MastodonAPI.StatusView
  23. alias Pleroma.Web.OAuth.OAuthController
  24. alias Pleroma.Web.Plugs.OAuthScopesPlug
  25. alias Pleroma.Web.Plugs.RateLimiter
  26. alias Pleroma.Web.TwitterAPI.TwitterAPI
  27. alias Pleroma.Web.Utils.Params
  28. plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
  29. plug(:skip_auth when action in [:create])
  30. plug(:skip_public_check when action in [:show, :statuses])
  31. plug(
  32. OAuthScopesPlug,
  33. %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
  34. when action in [:show, :followers, :following, :lookup, :endorsements]
  35. )
  36. plug(
  37. OAuthScopesPlug,
  38. %{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]}
  39. when action == :statuses
  40. )
  41. plug(
  42. OAuthScopesPlug,
  43. %{scopes: ["read:accounts"]}
  44. when action in [:verify_credentials, :endorsements, :own_endorsements]
  45. )
  46. plug(
  47. OAuthScopesPlug,
  48. %{scopes: ["write:accounts"]}
  49. when action in [:update_credentials, :note, :endorse, :unendorse]
  50. )
  51. plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
  52. plug(
  53. OAuthScopesPlug,
  54. %{scopes: ["follow", "read:blocks"]} when action == :blocks
  55. )
  56. plug(
  57. OAuthScopesPlug,
  58. %{scopes: ["follow", "write:blocks"]} when action in [:block, :unblock]
  59. )
  60. plug(
  61. OAuthScopesPlug,
  62. %{scopes: ["read:follows"]} when action in [:relationships, :familiar_followers]
  63. )
  64. plug(
  65. OAuthScopesPlug,
  66. %{scopes: ["follow", "write:follows"]}
  67. when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers]
  68. )
  69. plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
  70. plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
  71. @relationship_actions [:follow, :unfollow, :remove_from_followers]
  72. @needs_account ~W(
  73. followers following lists follow unfollow mute unmute block unblock
  74. note endorse unendorse endorsements remove_from_followers
  75. )a
  76. plug(
  77. RateLimiter,
  78. [name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions
  79. )
  80. plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions)
  81. plug(RateLimiter, [name: :app_account_creation] when action == :create)
  82. plug(:assign_account_by_id when action in @needs_account)
  83. action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
  84. defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation
  85. @doc "POST /api/v1/accounts"
  86. def create(
  87. %{assigns: %{app: app}, private: %{open_api_spex: %{body_params: params}}} = conn,
  88. _params
  89. ) do
  90. with :ok <- validate_email_param(params),
  91. :ok <- TwitterAPI.validate_captcha(app, params),
  92. {:ok, user} <- TwitterAPI.register_user(params),
  93. {_, {:ok, token}} <-
  94. {:login, OAuthController.login(user, app, app.scopes)} do
  95. OAuthController.after_token_exchange(conn, %{user: user, token: token})
  96. else
  97. {:login, {:account_status, :confirmation_pending}} ->
  98. json_response(conn, :ok, %{
  99. message: "You have been registered. Please check your email for further instructions.",
  100. identifier: "missing_confirmed_email"
  101. })
  102. {:login, {:account_status, :approval_pending}} ->
  103. json_response(conn, :ok, %{
  104. message:
  105. "You have been registered. You'll be able to log in once your account is approved.",
  106. identifier: "awaiting_approval"
  107. })
  108. {:login, _} ->
  109. json_response(conn, :ok, %{
  110. message:
  111. "You have been registered. Some post-registration steps may be pending. " <>
  112. "Please log in manually.",
  113. identifier: "manual_login_required"
  114. })
  115. {:error, error} ->
  116. json_response(conn, :bad_request, %{error: error})
  117. end
  118. end
  119. def create(%{assigns: %{app: _app}} = conn, _) do
  120. render_error(conn, :bad_request, "Missing parameters")
  121. end
  122. def create(conn, _) do
  123. render_error(conn, :forbidden, "Invalid credentials")
  124. end
  125. defp validate_email_param(%{email: email}) when not is_nil(email), do: :ok
  126. defp validate_email_param(_) do
  127. case Pleroma.Config.get([:instance, :account_activation_required]) do
  128. true -> {:error, dgettext("errors", "Missing parameter: %{name}", name: "email")}
  129. _ -> :ok
  130. end
  131. end
  132. @doc "GET /api/v1/accounts/verify_credentials"
  133. def verify_credentials(%{assigns: %{user: user}} = conn, _) do
  134. chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
  135. render(conn, "show.json",
  136. user: user,
  137. for: user,
  138. with_pleroma_settings: true,
  139. with_chat_token: chat_token
  140. )
  141. end
  142. @doc "PATCH /api/v1/accounts/update_credentials"
  143. def update_credentials(
  144. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: params}}} = conn,
  145. _params
  146. ) do
  147. params =
  148. params
  149. |> Enum.filter(fn {_, value} -> not is_nil(value) end)
  150. |> Enum.into(%{})
  151. # We use an empty string as a special value to reset
  152. # avatars, banners, backgrounds
  153. user_image_value = fn
  154. "" -> {:ok, nil}
  155. value -> {:ok, value}
  156. end
  157. user_params =
  158. [
  159. :no_rich_text,
  160. :hide_followers_count,
  161. :hide_follows_count,
  162. :hide_followers,
  163. :hide_follows,
  164. :hide_favorites,
  165. :show_role,
  166. :skip_thread_containment,
  167. :allow_following_move,
  168. :also_known_as,
  169. :accepts_chat_messages,
  170. :show_birthday
  171. ]
  172. |> Enum.reduce(%{}, fn key, acc ->
  173. Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
  174. end)
  175. |> Maps.put_if_present(:name, params[:display_name])
  176. |> Maps.put_if_present(:bio, params[:note])
  177. |> Maps.put_if_present(:raw_bio, params[:note])
  178. |> Maps.put_if_present(:avatar, params[:avatar], user_image_value)
  179. |> Maps.put_if_present(:banner, params[:header], user_image_value)
  180. |> Maps.put_if_present(:background, params[:pleroma_background_image], user_image_value)
  181. |> Maps.put_if_present(
  182. :raw_fields,
  183. params[:fields_attributes],
  184. &{:ok, normalize_fields_attributes(&1)}
  185. )
  186. |> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
  187. |> Maps.put_if_present(:default_scope, params[:default_scope])
  188. |> Maps.put_if_present(:default_scope, params["source"]["privacy"])
  189. |> Maps.put_if_present(:actor_type, params[:bot], fn bot ->
  190. if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
  191. end)
  192. |> Maps.put_if_present(:actor_type, params[:actor_type])
  193. |> Maps.put_if_present(:also_known_as, params[:also_known_as])
  194. # Note: param name is indeed :locked (not an error)
  195. |> Maps.put_if_present(:is_locked, params[:locked])
  196. # Note: param name is indeed :discoverable (not an error)
  197. |> Maps.put_if_present(:is_discoverable, params[:discoverable])
  198. |> Maps.put_if_present(:birthday, params[:birthday])
  199. |> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
  200. |> Maps.put_if_present(:avatar_description, params[:avatar_description])
  201. |> Maps.put_if_present(:header_description, params[:header_description])
  202. # What happens here:
  203. #
  204. # We want to update the user through the pipeline, but the ActivityPub
  205. # update information is not quite enough for this, because this also
  206. # contains local settings that don't federate and don't even appear
  207. # in the Update activity.
  208. #
  209. # So we first build the normal local changeset, then apply it to the
  210. # user data, but don't persist it. With this, we generate the object
  211. # data for our update activity. We feed this and the changeset as meta
  212. # information into the pipeline, where they will be properly updated and
  213. # federated.
  214. with changeset <- User.update_changeset(user, user_params),
  215. {:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update),
  216. updated_object <-
  217. Pleroma.Web.ActivityPub.UserView.render("user.json", user: unpersisted_user)
  218. |> Map.delete("@context"),
  219. {:ok, update_data, []} <- Builder.update(user, updated_object),
  220. {:ok, _update, _} <-
  221. Pipeline.common_pipeline(update_data,
  222. local: true,
  223. user_update_changeset: changeset
  224. ) do
  225. render(conn, "show.json",
  226. user: unpersisted_user,
  227. for: unpersisted_user,
  228. with_pleroma_settings: true
  229. )
  230. else
  231. {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} ->
  232. render_error(conn, :request_entity_too_large, "File is too large")
  233. {:error, %Ecto.Changeset{errors: [banner: {"file is too large", _}]}} ->
  234. render_error(conn, :request_entity_too_large, "File is too large")
  235. {:error, %Ecto.Changeset{errors: [background: {"file is too large", _}]}} ->
  236. render_error(conn, :request_entity_too_large, "File is too large")
  237. {:error, %Ecto.Changeset{errors: [{:bio, {_, _}} | _]}} ->
  238. render_error(conn, :request_entity_too_large, "Bio is too long")
  239. {:error, %Ecto.Changeset{errors: [{:name, {_, _}} | _]}} ->
  240. render_error(conn, :request_entity_too_large, "Name is too long")
  241. {:error, %Ecto.Changeset{errors: [{:avatar_description, {_, _}} | _]}} ->
  242. render_error(conn, :request_entity_too_large, "Avatar description is too long")
  243. {:error, %Ecto.Changeset{errors: [{:header_description, {_, _}} | _]}} ->
  244. render_error(conn, :request_entity_too_large, "Banner description is too long")
  245. {:error, %Ecto.Changeset{errors: [{:fields, {"invalid", _}} | _]}} ->
  246. render_error(conn, :request_entity_too_large, "One or more field entries are too long")
  247. {:error, %Ecto.Changeset{errors: [{:fields, {_, _}} | _]}} ->
  248. render_error(conn, :request_entity_too_large, "Too many field entries")
  249. _e ->
  250. render_error(conn, :forbidden, "Invalid request")
  251. end
  252. end
  253. defp normalize_fields_attributes(fields) do
  254. if(Enum.all?(fields, &is_tuple/1), do: Enum.map(fields, fn {_, v} -> v end), else: fields)
  255. |> Enum.map(fn
  256. %{} = field -> %{"name" => field.name, "value" => field.value}
  257. field -> field
  258. end)
  259. end
  260. @doc "GET /api/v1/accounts/relationships"
  261. def relationships(
  262. %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn,
  263. _
  264. ) do
  265. targets = User.get_all_by_ids(List.wrap(id))
  266. render(conn, "relationships.json", user: user, targets: targets)
  267. end
  268. # Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array.
  269. def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
  270. @doc "GET /api/v1/accounts/:id"
  271. def show(
  272. %{
  273. assigns: %{user: for_user},
  274. private: %{open_api_spex: %{params: %{id: nickname_or_id} = params}}
  275. } = conn,
  276. _params
  277. ) do
  278. with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
  279. :visible <- User.visible_for(user, for_user) do
  280. render(conn, "show.json",
  281. user: user,
  282. for: for_user,
  283. embed_relationships: embed_relationships?(params)
  284. )
  285. else
  286. error -> user_visibility_error(conn, error)
  287. end
  288. end
  289. @doc "GET /api/v1/accounts/:id/statuses"
  290. def statuses(
  291. %{assigns: %{user: reading_user}, private: %{open_api_spex: %{params: params}}} = conn,
  292. _params
  293. ) do
  294. with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
  295. :visible <- User.visible_for(user, reading_user) do
  296. params =
  297. params
  298. |> Map.delete(:tagged)
  299. |> Map.put(:tag, params[:tagged])
  300. activities = ActivityPub.fetch_user_activities(user, reading_user, params)
  301. conn
  302. |> add_link_headers(activities)
  303. |> put_view(StatusView)
  304. |> render("index.json",
  305. activities: activities,
  306. for: reading_user,
  307. as: :activity,
  308. with_muted: Map.get(params, :with_muted, false)
  309. )
  310. else
  311. error -> user_visibility_error(conn, error)
  312. end
  313. end
  314. defp user_visibility_error(conn, error) do
  315. case error do
  316. :restrict_unauthenticated ->
  317. render_error(conn, :unauthorized, "This API requires an authenticated user")
  318. _ ->
  319. render_error(conn, :not_found, "Can't find user")
  320. end
  321. end
  322. @doc "GET /api/v1/accounts/:id/followers"
  323. def followers(
  324. %{assigns: %{user: for_user, account: user}, private: %{open_api_spex: %{params: params}}} =
  325. conn,
  326. _params
  327. ) do
  328. params =
  329. params
  330. |> Enum.map(fn {key, value} -> {to_string(key), value} end)
  331. |> Enum.into(%{})
  332. followers =
  333. cond do
  334. for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params)
  335. user.hide_followers -> []
  336. true -> MastodonAPI.get_followers(user, params)
  337. end
  338. conn
  339. |> add_link_headers(followers)
  340. # https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
  341. |> render("index.json",
  342. for: for_user,
  343. users: followers,
  344. as: :user,
  345. embed_relationships: embed_relationships?(params)
  346. )
  347. end
  348. @doc "GET /api/v1/accounts/:id/following"
  349. def following(
  350. %{assigns: %{user: for_user, account: user}, private: %{open_api_spex: %{params: params}}} =
  351. conn,
  352. _params
  353. ) do
  354. params =
  355. params
  356. |> Enum.map(fn {key, value} -> {to_string(key), value} end)
  357. |> Enum.into(%{})
  358. followers =
  359. cond do
  360. for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params)
  361. user.hide_follows -> []
  362. true -> MastodonAPI.get_friends(user, params)
  363. end
  364. conn
  365. |> add_link_headers(followers)
  366. # https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
  367. |> render("index.json",
  368. for: for_user,
  369. users: followers,
  370. as: :user,
  371. embed_relationships: embed_relationships?(params)
  372. )
  373. end
  374. @doc "GET /api/v1/accounts/:id/lists"
  375. def lists(%{assigns: %{user: user, account: account}} = conn, _params) do
  376. lists = Pleroma.List.get_lists_account_belongs(user, account)
  377. conn
  378. |> put_view(ListView)
  379. |> render("index.json", lists: lists)
  380. end
  381. @doc "POST /api/v1/accounts/:id/follow"
  382. def follow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
  383. {:error, "Can not follow yourself"}
  384. end
  385. def follow(
  386. %{
  387. assigns: %{user: follower, account: followed},
  388. private: %{open_api_spex: %{body_params: params}}
  389. } = conn,
  390. _
  391. ) do
  392. with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do
  393. render(conn, "relationship.json", user: follower, target: followed)
  394. else
  395. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  396. end
  397. end
  398. @doc "POST /api/v1/accounts/:id/unfollow"
  399. def unfollow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
  400. {:error, "Can not unfollow yourself"}
  401. end
  402. def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) do
  403. with {:ok, follower} <- CommonAPI.unfollow(followed, follower) do
  404. render(conn, "relationship.json", user: follower, target: followed)
  405. end
  406. end
  407. @doc "POST /api/v1/accounts/:id/mute"
  408. def mute(
  409. %{
  410. assigns: %{user: muter, account: muted},
  411. private: %{open_api_spex: %{body_params: params}}
  412. } = conn,
  413. _params
  414. ) do
  415. params =
  416. params
  417. |> Map.put_new(:duration, Map.get(params, :expires_in, 0))
  418. with {:ok, _user_relationships} <- User.mute(muter, muted, params) do
  419. render(conn, "relationship.json", user: muter, target: muted)
  420. else
  421. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  422. end
  423. end
  424. @doc "POST /api/v1/accounts/:id/unmute"
  425. def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do
  426. with {:ok, _user_relationships} <- User.unmute(muter, muted) do
  427. render(conn, "relationship.json", user: muter, target: muted)
  428. else
  429. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  430. end
  431. end
  432. @doc "POST /api/v1/accounts/:id/block"
  433. def block(
  434. %{
  435. assigns: %{user: blocker, account: blocked},
  436. private: %{open_api_spex: %{body_params: params}}
  437. } = conn,
  438. _params
  439. ) do
  440. with {:ok, _activity} <- CommonAPI.block(blocked, blocker, params) do
  441. render(conn, "relationship.json", user: blocker, target: blocked)
  442. else
  443. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  444. end
  445. end
  446. @doc "POST /api/v1/accounts/:id/unblock"
  447. def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
  448. with {:ok, _activity} <- CommonAPI.unblock(blocked, blocker) do
  449. render(conn, "relationship.json", user: blocker, target: blocked)
  450. else
  451. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  452. end
  453. end
  454. @doc "POST /api/v1/accounts/:id/note"
  455. def note(
  456. %{
  457. assigns: %{user: noter, account: target},
  458. private: %{open_api_spex: %{body_params: %{comment: comment}}}
  459. } = conn,
  460. _params
  461. ) do
  462. with {:ok, _user_note} <- UserNote.create(noter, target, comment) do
  463. render(conn, "relationship.json", user: noter, target: target)
  464. end
  465. end
  466. @doc "POST /api/v1/accounts/:id/pin"
  467. def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
  468. with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
  469. render(conn, "relationship.json", user: endorser, target: endorsed)
  470. else
  471. {:error, message} -> json_response(conn, :bad_request, %{error: message})
  472. end
  473. end
  474. @doc "POST /api/v1/accounts/:id/unpin"
  475. def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
  476. with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do
  477. render(conn, "relationship.json", user: endorser, target: endorsed)
  478. else
  479. {:error, message} -> json_response(conn, :forbidden, %{error: message})
  480. end
  481. end
  482. @doc "GET /api/v1/accounts/:id/endorsements"
  483. def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
  484. users =
  485. user
  486. |> User.endorsed_users_relation(_restrict_deactivated = true)
  487. |> Pleroma.Repo.all()
  488. conn
  489. |> render("index.json",
  490. for: for_user,
  491. users: users,
  492. as: :user,
  493. embed_relationships: embed_relationships?(params)
  494. )
  495. end
  496. @doc "POST /api/v1/accounts/:id/remove_from_followers"
  497. def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
  498. {:error, "Can not unfollow yourself"}
  499. end
  500. def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do
  501. with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
  502. render(conn, "relationship.json", user: followed, target: follower)
  503. else
  504. nil ->
  505. render_error(conn, :not_found, "Record not found")
  506. end
  507. end
  508. @doc "POST /api/v1/follows"
  509. def follow_by_uri(%{private: %{open_api_spex: %{body_params: %{uri: uri}}}} = conn, _) do
  510. case User.get_cached_by_nickname(uri) do
  511. %User{} = user ->
  512. conn
  513. |> assign(:account, user)
  514. |> follow(%{})
  515. nil ->
  516. {:error, :not_found}
  517. end
  518. end
  519. @doc "GET /api/v1/mutes"
  520. def mutes(%{assigns: %{user: user}} = conn, params) do
  521. users =
  522. user
  523. |> User.muted_users_relation(_restrict_deactivated = true)
  524. |> Pleroma.Pagination.fetch_paginated(params)
  525. conn
  526. |> add_link_headers(users)
  527. |> render("index.json",
  528. users: users,
  529. for: user,
  530. as: :user,
  531. embed_relationships: embed_relationships?(params),
  532. mutes: true
  533. )
  534. end
  535. @doc "GET /api/v1/blocks"
  536. def blocks(%{assigns: %{user: user}} = conn, params) do
  537. users =
  538. user
  539. |> User.blocked_users_relation(_restrict_deactivated = true)
  540. |> Pleroma.Pagination.fetch_paginated(params)
  541. conn
  542. |> add_link_headers(users)
  543. |> render("index.json",
  544. users: users,
  545. for: user,
  546. as: :user,
  547. embed_relationships: embed_relationships?(params),
  548. blocks: true
  549. )
  550. end
  551. @doc "GET /api/v1/accounts/lookup"
  552. def lookup(
  553. %{assigns: %{user: for_user}, private: %{open_api_spex: %{params: %{acct: nickname}}}} =
  554. conn,
  555. _params
  556. ) do
  557. with %User{} = user <- User.get_by_nickname(nickname),
  558. :visible <- User.visible_for(user, for_user) do
  559. render(conn, "show.json",
  560. user: user,
  561. skip_visibility_check: true
  562. )
  563. else
  564. error -> user_visibility_error(conn, error)
  565. end
  566. end
  567. @doc "GET /api/v1/endorsements"
  568. def own_endorsements(%{assigns: %{user: user}} = conn, params) do
  569. users =
  570. user
  571. |> User.endorsed_users_relation(_restrict_deactivated = true)
  572. |> Pleroma.Repo.all()
  573. conn
  574. |> render("index.json",
  575. users: users,
  576. for: user,
  577. as: :user,
  578. embed_relationships: embed_relationships?(params)
  579. )
  580. end
  581. @doc "GET /api/v1/accounts/familiar_followers"
  582. def familiar_followers(
  583. %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn,
  584. _id
  585. ) do
  586. users =
  587. User.get_all_by_ids(List.wrap(id))
  588. |> Enum.map(&%{id: &1.id, accounts: get_familiar_followers(&1, user)})
  589. conn
  590. |> render("familiar_followers.json",
  591. for: user,
  592. users: users,
  593. as: :user
  594. )
  595. end
  596. defp get_familiar_followers(%{id: id} = user, %{id: id}) do
  597. User.get_familiar_followers(user, user)
  598. end
  599. defp get_familiar_followers(%{hide_followers: true}, _current_user) do
  600. []
  601. end
  602. defp get_familiar_followers(user, current_user) do
  603. User.get_familiar_followers(user, current_user)
  604. end
  605. end