Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1034 lines
27KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.AdminAPI.AdminAPIController do
  5. use Pleroma.Web, :controller
  6. import Pleroma.Web.ControllerHelper, only: [json_response: 3]
  7. alias Pleroma.Activity
  8. alias Pleroma.Config
  9. alias Pleroma.ConfigDB
  10. alias Pleroma.ModerationLog
  11. alias Pleroma.Plugs.OAuthScopesPlug
  12. alias Pleroma.ReportNote
  13. alias Pleroma.Stats
  14. alias Pleroma.User
  15. alias Pleroma.UserInviteToken
  16. alias Pleroma.Web.ActivityPub.ActivityPub
  17. alias Pleroma.Web.ActivityPub.Relay
  18. alias Pleroma.Web.ActivityPub.Utils
  19. alias Pleroma.Web.AdminAPI.AccountView
  20. alias Pleroma.Web.AdminAPI.ConfigView
  21. alias Pleroma.Web.AdminAPI.ModerationLogView
  22. alias Pleroma.Web.AdminAPI.Report
  23. alias Pleroma.Web.AdminAPI.ReportView
  24. alias Pleroma.Web.AdminAPI.Search
  25. alias Pleroma.Web.CommonAPI
  26. alias Pleroma.Web.Endpoint
  27. alias Pleroma.Web.MastodonAPI.StatusView
  28. alias Pleroma.Web.Router
  29. require Logger
  30. @descriptions_json Pleroma.Docs.JSON.compile()
  31. @users_page_size 50
  32. plug(
  33. OAuthScopesPlug,
  34. %{scopes: ["read:accounts"], admin: true}
  35. when action in [:list_users, :user_show, :right_get]
  36. )
  37. plug(
  38. OAuthScopesPlug,
  39. %{scopes: ["write:accounts"], admin: true}
  40. when action in [
  41. :get_password_reset,
  42. :user_delete,
  43. :users_create,
  44. :user_toggle_activation,
  45. :user_activate,
  46. :user_deactivate,
  47. :tag_users,
  48. :untag_users,
  49. :right_add,
  50. :right_delete
  51. ]
  52. )
  53. plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
  54. plug(
  55. OAuthScopesPlug,
  56. %{scopes: ["write:invites"], admin: true}
  57. when action in [:create_invite_token, :revoke_invite, :email_invite]
  58. )
  59. plug(
  60. OAuthScopesPlug,
  61. %{scopes: ["write:follows"], admin: true}
  62. when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
  63. )
  64. plug(
  65. OAuthScopesPlug,
  66. %{scopes: ["read:reports"], admin: true}
  67. when action in [:list_reports, :report_show]
  68. )
  69. plug(
  70. OAuthScopesPlug,
  71. %{scopes: ["write:reports"], admin: true}
  72. when action in [:reports_update]
  73. )
  74. plug(
  75. OAuthScopesPlug,
  76. %{scopes: ["read:statuses"], admin: true}
  77. when action == :list_user_statuses
  78. )
  79. plug(
  80. OAuthScopesPlug,
  81. %{scopes: ["write:statuses"], admin: true}
  82. when action in [:status_update, :status_delete]
  83. )
  84. plug(
  85. OAuthScopesPlug,
  86. %{scopes: ["read"], admin: true}
  87. when action in [:config_show, :list_log, :stats]
  88. )
  89. plug(
  90. OAuthScopesPlug,
  91. %{scopes: ["write"], admin: true}
  92. when action == :config_update
  93. )
  94. action_fallback(:errors)
  95. def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
  96. user = User.get_cached_by_nickname(nickname)
  97. User.delete(user)
  98. ModerationLog.insert_log(%{
  99. actor: admin,
  100. subject: [user],
  101. action: "delete"
  102. })
  103. conn
  104. |> json(nickname)
  105. end
  106. def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  107. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  108. User.delete(users)
  109. ModerationLog.insert_log(%{
  110. actor: admin,
  111. subject: users,
  112. action: "delete"
  113. })
  114. conn
  115. |> json(nicknames)
  116. end
  117. def user_follow(%{assigns: %{user: admin}} = conn, %{
  118. "follower" => follower_nick,
  119. "followed" => followed_nick
  120. }) do
  121. with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
  122. %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
  123. User.follow(follower, followed)
  124. ModerationLog.insert_log(%{
  125. actor: admin,
  126. followed: followed,
  127. follower: follower,
  128. action: "follow"
  129. })
  130. end
  131. conn
  132. |> json("ok")
  133. end
  134. def user_unfollow(%{assigns: %{user: admin}} = conn, %{
  135. "follower" => follower_nick,
  136. "followed" => followed_nick
  137. }) do
  138. with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
  139. %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
  140. User.unfollow(follower, followed)
  141. ModerationLog.insert_log(%{
  142. actor: admin,
  143. followed: followed,
  144. follower: follower,
  145. action: "unfollow"
  146. })
  147. end
  148. conn
  149. |> json("ok")
  150. end
  151. def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
  152. changesets =
  153. Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
  154. user_data = %{
  155. nickname: nickname,
  156. name: nickname,
  157. email: email,
  158. password: password,
  159. password_confirmation: password,
  160. bio: "."
  161. }
  162. User.register_changeset(%User{}, user_data, need_confirmation: false)
  163. end)
  164. |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
  165. Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
  166. end)
  167. case Pleroma.Repo.transaction(changesets) do
  168. {:ok, users} ->
  169. res =
  170. users
  171. |> Map.values()
  172. |> Enum.map(fn user ->
  173. {:ok, user} = User.post_register_action(user)
  174. user
  175. end)
  176. |> Enum.map(&AccountView.render("created.json", %{user: &1}))
  177. ModerationLog.insert_log(%{
  178. actor: admin,
  179. subjects: Map.values(users),
  180. action: "create"
  181. })
  182. conn
  183. |> json(res)
  184. {:error, id, changeset, _} ->
  185. res =
  186. Enum.map(changesets.operations, fn
  187. {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
  188. AccountView.render("create-error.json", %{changeset: changeset})
  189. {_, {:changeset, current_changeset, _}} ->
  190. AccountView.render("create-error.json", %{changeset: current_changeset})
  191. end)
  192. conn
  193. |> put_status(:conflict)
  194. |> json(res)
  195. end
  196. end
  197. def user_show(conn, %{"nickname" => nickname}) do
  198. with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
  199. conn
  200. |> put_view(AccountView)
  201. |> render("show.json", %{user: user})
  202. else
  203. _ -> {:error, :not_found}
  204. end
  205. end
  206. def list_instance_statuses(conn, %{"instance" => instance} = params) do
  207. with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
  208. {page, page_size} = page_params(params)
  209. activities =
  210. ActivityPub.fetch_statuses(nil, %{
  211. "instance" => instance,
  212. "limit" => page_size,
  213. "offset" => (page - 1) * page_size,
  214. "exclude_reblogs" => !with_reblogs && "true"
  215. })
  216. conn
  217. |> put_view(Pleroma.Web.AdminAPI.StatusView)
  218. |> render("index.json", %{activities: activities, as: :activity})
  219. end
  220. def list_user_statuses(conn, %{"nickname" => nickname} = params) do
  221. with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
  222. godmode = params["godmode"] == "true" || params["godmode"] == true
  223. with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
  224. {_, page_size} = page_params(params)
  225. activities =
  226. ActivityPub.fetch_user_activities(user, nil, %{
  227. "limit" => page_size,
  228. "godmode" => godmode,
  229. "exclude_reblogs" => !with_reblogs && "true"
  230. })
  231. conn
  232. |> put_view(StatusView)
  233. |> render("index.json", %{activities: activities, as: :activity})
  234. else
  235. _ -> {:error, :not_found}
  236. end
  237. end
  238. def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
  239. user = User.get_cached_by_nickname(nickname)
  240. {:ok, updated_user} = User.deactivate(user, !user.deactivated)
  241. action = if user.deactivated, do: "activate", else: "deactivate"
  242. ModerationLog.insert_log(%{
  243. actor: admin,
  244. subject: [user],
  245. action: action
  246. })
  247. conn
  248. |> put_view(AccountView)
  249. |> render("show.json", %{user: updated_user})
  250. end
  251. def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  252. users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
  253. {:ok, updated_users} = User.deactivate(users, false)
  254. ModerationLog.insert_log(%{
  255. actor: admin,
  256. subject: users,
  257. action: "activate"
  258. })
  259. conn
  260. |> put_view(AccountView)
  261. |> render("index.json", %{users: Keyword.values(updated_users)})
  262. end
  263. def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  264. users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
  265. {:ok, updated_users} = User.deactivate(users, true)
  266. ModerationLog.insert_log(%{
  267. actor: admin,
  268. subject: users,
  269. action: "deactivate"
  270. })
  271. conn
  272. |> put_view(AccountView)
  273. |> render("index.json", %{users: Keyword.values(updated_users)})
  274. end
  275. def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
  276. with {:ok, _} <- User.tag(nicknames, tags) do
  277. ModerationLog.insert_log(%{
  278. actor: admin,
  279. nicknames: nicknames,
  280. tags: tags,
  281. action: "tag"
  282. })
  283. json_response(conn, :no_content, "")
  284. end
  285. end
  286. def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
  287. with {:ok, _} <- User.untag(nicknames, tags) do
  288. ModerationLog.insert_log(%{
  289. actor: admin,
  290. nicknames: nicknames,
  291. tags: tags,
  292. action: "untag"
  293. })
  294. json_response(conn, :no_content, "")
  295. end
  296. end
  297. def list_users(conn, params) do
  298. {page, page_size} = page_params(params)
  299. filters = maybe_parse_filters(params["filters"])
  300. search_params = %{
  301. query: params["query"],
  302. page: page,
  303. page_size: page_size,
  304. tags: params["tags"],
  305. name: params["name"],
  306. email: params["email"]
  307. }
  308. with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
  309. {:ok, users, count} <- filter_service_users(users, count),
  310. do:
  311. conn
  312. |> json(
  313. AccountView.render("index.json",
  314. users: users,
  315. count: count,
  316. page_size: page_size
  317. )
  318. )
  319. end
  320. defp filter_service_users(users, count) do
  321. filtered_users = Enum.reject(users, &service_user?/1)
  322. count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
  323. {:ok, filtered_users, count}
  324. end
  325. defp service_user?(user) do
  326. String.match?(user.ap_id, ~r/.*\/relay$/) or
  327. String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
  328. end
  329. @filters ~w(local external active deactivated is_admin is_moderator)
  330. @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
  331. defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
  332. defp maybe_parse_filters(filters) do
  333. filters
  334. |> String.split(",")
  335. |> Enum.filter(&Enum.member?(@filters, &1))
  336. |> Enum.map(&String.to_atom(&1))
  337. |> Enum.into(%{}, &{&1, true})
  338. end
  339. def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
  340. "permission_group" => permission_group,
  341. "nicknames" => nicknames
  342. })
  343. when permission_group in ["moderator", "admin"] do
  344. update = %{:"is_#{permission_group}" => true}
  345. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  346. for u <- users, do: User.admin_api_update(u, update)
  347. ModerationLog.insert_log(%{
  348. action: "grant",
  349. actor: admin,
  350. subject: users,
  351. permission: permission_group
  352. })
  353. json(conn, update)
  354. end
  355. def right_add_multiple(conn, _) do
  356. render_error(conn, :not_found, "No such permission_group")
  357. end
  358. def right_add(%{assigns: %{user: admin}} = conn, %{
  359. "permission_group" => permission_group,
  360. "nickname" => nickname
  361. })
  362. when permission_group in ["moderator", "admin"] do
  363. fields = %{:"is_#{permission_group}" => true}
  364. {:ok, user} =
  365. nickname
  366. |> User.get_cached_by_nickname()
  367. |> User.admin_api_update(fields)
  368. ModerationLog.insert_log(%{
  369. action: "grant",
  370. actor: admin,
  371. subject: [user],
  372. permission: permission_group
  373. })
  374. json(conn, fields)
  375. end
  376. def right_add(conn, _) do
  377. render_error(conn, :not_found, "No such permission_group")
  378. end
  379. def right_get(conn, %{"nickname" => nickname}) do
  380. user = User.get_cached_by_nickname(nickname)
  381. conn
  382. |> json(%{
  383. is_moderator: user.is_moderator,
  384. is_admin: user.is_admin
  385. })
  386. end
  387. def right_delete_multiple(
  388. %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
  389. %{
  390. "permission_group" => permission_group,
  391. "nicknames" => nicknames
  392. }
  393. )
  394. when permission_group in ["moderator", "admin"] do
  395. with false <- Enum.member?(nicknames, admin_nickname) do
  396. update = %{:"is_#{permission_group}" => false}
  397. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  398. for u <- users, do: User.admin_api_update(u, update)
  399. ModerationLog.insert_log(%{
  400. action: "revoke",
  401. actor: admin,
  402. subject: users,
  403. permission: permission_group
  404. })
  405. json(conn, update)
  406. else
  407. _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
  408. end
  409. end
  410. def right_delete_multiple(conn, _) do
  411. render_error(conn, :not_found, "No such permission_group")
  412. end
  413. def right_delete(
  414. %{assigns: %{user: admin}} = conn,
  415. %{
  416. "permission_group" => permission_group,
  417. "nickname" => nickname
  418. }
  419. )
  420. when permission_group in ["moderator", "admin"] do
  421. fields = %{:"is_#{permission_group}" => false}
  422. {:ok, user} =
  423. nickname
  424. |> User.get_cached_by_nickname()
  425. |> User.admin_api_update(fields)
  426. ModerationLog.insert_log(%{
  427. action: "revoke",
  428. actor: admin,
  429. subject: [user],
  430. permission: permission_group
  431. })
  432. json(conn, fields)
  433. end
  434. def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
  435. render_error(conn, :forbidden, "You can't revoke your own admin status.")
  436. end
  437. def relay_list(conn, _params) do
  438. with {:ok, list} <- Relay.list() do
  439. json(conn, %{relays: list})
  440. else
  441. _ ->
  442. conn
  443. |> put_status(500)
  444. end
  445. end
  446. def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
  447. with {:ok, _message} <- Relay.follow(target) do
  448. ModerationLog.insert_log(%{
  449. action: "relay_follow",
  450. actor: admin,
  451. target: target
  452. })
  453. json(conn, target)
  454. else
  455. _ ->
  456. conn
  457. |> put_status(500)
  458. |> json(target)
  459. end
  460. end
  461. def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
  462. with {:ok, _message} <- Relay.unfollow(target) do
  463. ModerationLog.insert_log(%{
  464. action: "relay_unfollow",
  465. actor: admin,
  466. target: target
  467. })
  468. json(conn, target)
  469. else
  470. _ ->
  471. conn
  472. |> put_status(500)
  473. |> json(target)
  474. end
  475. end
  476. @doc "Sends registration invite via email"
  477. def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
  478. with true <-
  479. Config.get([:instance, :invites_enabled]) &&
  480. !Config.get([:instance, :registrations_open]),
  481. {:ok, invite_token} <- UserInviteToken.create_invite(),
  482. email <-
  483. Pleroma.Emails.UserEmail.user_invitation_email(
  484. user,
  485. invite_token,
  486. email,
  487. params["name"]
  488. ),
  489. {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
  490. json_response(conn, :no_content, "")
  491. end
  492. end
  493. @doc "Create an account registration invite token"
  494. def create_invite_token(conn, params) do
  495. opts = %{}
  496. opts =
  497. if params["max_use"],
  498. do: Map.put(opts, :max_use, params["max_use"]),
  499. else: opts
  500. opts =
  501. if params["expires_at"],
  502. do: Map.put(opts, :expires_at, params["expires_at"]),
  503. else: opts
  504. {:ok, invite} = UserInviteToken.create_invite(opts)
  505. json(conn, AccountView.render("invite.json", %{invite: invite}))
  506. end
  507. @doc "Get list of created invites"
  508. def invites(conn, _params) do
  509. invites = UserInviteToken.list_invites()
  510. conn
  511. |> put_view(AccountView)
  512. |> render("invites.json", %{invites: invites})
  513. end
  514. @doc "Revokes invite by token"
  515. def revoke_invite(conn, %{"token" => token}) do
  516. with {:ok, invite} <- UserInviteToken.find_by_token(token),
  517. {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
  518. conn
  519. |> put_view(AccountView)
  520. |> render("invite.json", %{invite: updated_invite})
  521. else
  522. nil -> {:error, :not_found}
  523. end
  524. end
  525. @doc "Get a password reset token (base64 string) for given nickname"
  526. def get_password_reset(conn, %{"nickname" => nickname}) do
  527. (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
  528. {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
  529. conn
  530. |> json(%{
  531. token: token.token,
  532. link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
  533. })
  534. end
  535. @doc "Force password reset for a given user"
  536. def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  537. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  538. Enum.each(users, &User.force_password_reset_async/1)
  539. ModerationLog.insert_log(%{
  540. actor: admin,
  541. subject: users,
  542. action: "force_password_reset"
  543. })
  544. json_response(conn, :no_content, "")
  545. end
  546. def list_reports(conn, params) do
  547. {page, page_size} = page_params(params)
  548. reports = Utils.get_reports(params, page, page_size)
  549. conn
  550. |> put_view(ReportView)
  551. |> render("index.json", %{reports: reports})
  552. end
  553. def list_grouped_reports(conn, _params) do
  554. statuses = Utils.get_reported_activities()
  555. conn
  556. |> put_view(ReportView)
  557. |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
  558. end
  559. def report_show(conn, %{"id" => id}) do
  560. with %Activity{} = report <- Activity.get_by_id(id) do
  561. conn
  562. |> put_view(ReportView)
  563. |> render("show.json", Report.extract_report_info(report))
  564. else
  565. _ -> {:error, :not_found}
  566. end
  567. end
  568. def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
  569. result =
  570. reports
  571. |> Enum.map(fn report ->
  572. with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
  573. ModerationLog.insert_log(%{
  574. action: "report_update",
  575. actor: admin,
  576. subject: activity
  577. })
  578. activity
  579. else
  580. {:error, message} -> %{id: report["id"], error: message}
  581. end
  582. end)
  583. case Enum.any?(result, &Map.has_key?(&1, :error)) do
  584. true -> json_response(conn, :bad_request, result)
  585. false -> json_response(conn, :no_content, "")
  586. end
  587. end
  588. def report_notes_create(%{assigns: %{user: user}} = conn, %{
  589. "id" => report_id,
  590. "content" => content
  591. }) do
  592. with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
  593. ModerationLog.insert_log(%{
  594. action: "report_note",
  595. actor: user,
  596. subject: Activity.get_by_id(report_id),
  597. text: content
  598. })
  599. json_response(conn, :no_content, "")
  600. else
  601. _ -> json_response(conn, :bad_request, "")
  602. end
  603. end
  604. def report_notes_delete(%{assigns: %{user: user}} = conn, %{
  605. "id" => note_id,
  606. "report_id" => report_id
  607. }) do
  608. with {:ok, note} <- ReportNote.destroy(note_id) do
  609. ModerationLog.insert_log(%{
  610. action: "report_note_delete",
  611. actor: user,
  612. subject: Activity.get_by_id(report_id),
  613. text: note.content
  614. })
  615. json_response(conn, :no_content, "")
  616. else
  617. _ -> json_response(conn, :bad_request, "")
  618. end
  619. end
  620. def list_statuses(%{assigns: %{user: admin}} = conn, params) do
  621. godmode = params["godmode"] == "true" || params["godmode"] == true
  622. local_only = params["local_only"] == "true" || params["local_only"] == true
  623. with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
  624. {page, page_size} = page_params(params)
  625. activities =
  626. ActivityPub.fetch_statuses(admin, %{
  627. "godmode" => godmode,
  628. "local_only" => local_only,
  629. "limit" => page_size,
  630. "offset" => (page - 1) * page_size,
  631. "exclude_reblogs" => !with_reblogs && "true"
  632. })
  633. conn
  634. |> put_view(Pleroma.Web.AdminAPI.StatusView)
  635. |> render("index.json", %{activities: activities, as: :activity})
  636. end
  637. def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
  638. with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
  639. {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
  640. ModerationLog.insert_log(%{
  641. action: "status_update",
  642. actor: admin,
  643. subject: activity,
  644. sensitive: sensitive,
  645. visibility: params["visibility"]
  646. })
  647. conn
  648. |> put_view(StatusView)
  649. |> render("show.json", %{activity: activity})
  650. end
  651. end
  652. def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
  653. with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
  654. ModerationLog.insert_log(%{
  655. action: "status_delete",
  656. actor: user,
  657. subject_id: id
  658. })
  659. json(conn, %{})
  660. end
  661. end
  662. def list_log(conn, params) do
  663. {page, page_size} = page_params(params)
  664. log =
  665. ModerationLog.get_all(%{
  666. page: page,
  667. page_size: page_size,
  668. start_date: params["start_date"],
  669. end_date: params["end_date"],
  670. user_id: params["user_id"],
  671. search: params["search"]
  672. })
  673. conn
  674. |> put_view(ModerationLogView)
  675. |> render("index.json", %{log: log})
  676. end
  677. def config_descriptions(conn, _params) do
  678. conn
  679. |> Plug.Conn.put_resp_content_type("application/json")
  680. |> Plug.Conn.send_resp(200, @descriptions_json)
  681. end
  682. def config_show(conn, %{"only_db" => true}) do
  683. with :ok <- configurable_from_database(conn) do
  684. configs = Pleroma.Repo.all(ConfigDB)
  685. conn
  686. |> put_view(ConfigView)
  687. |> render("index.json", %{configs: configs})
  688. end
  689. end
  690. def config_show(conn, _params) do
  691. with :ok <- configurable_from_database(conn) do
  692. configs = ConfigDB.get_all_as_keyword()
  693. merged =
  694. Config.Holder.config()
  695. |> ConfigDB.merge(configs)
  696. |> Enum.map(fn {group, values} ->
  697. Enum.map(values, fn {key, value} ->
  698. db =
  699. if configs[group][key] do
  700. ConfigDB.get_db_keys(configs[group][key], key)
  701. end
  702. db_value = configs[group][key]
  703. merged_value =
  704. if !is_nil(db_value) and Keyword.keyword?(db_value) and
  705. ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
  706. ConfigDB.merge_group(group, key, value, db_value)
  707. else
  708. value
  709. end
  710. setting = %{
  711. group: ConfigDB.convert(group),
  712. key: ConfigDB.convert(key),
  713. value: ConfigDB.convert(merged_value)
  714. }
  715. if db, do: Map.put(setting, :db, db), else: setting
  716. end)
  717. end)
  718. |> List.flatten()
  719. response = %{configs: merged}
  720. response =
  721. if Restarter.Pleroma.need_reboot?() do
  722. Map.put(response, :need_reboot, true)
  723. else
  724. response
  725. end
  726. json(conn, response)
  727. end
  728. end
  729. def config_update(conn, %{"configs" => configs}) do
  730. with :ok <- configurable_from_database(conn) do
  731. {_errors, results} =
  732. Enum.map(configs, fn
  733. %{"group" => group, "key" => key, "delete" => true} = params ->
  734. ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
  735. %{"group" => group, "key" => key, "value" => value} ->
  736. ConfigDB.update_or_create(%{group: group, key: key, value: value})
  737. end)
  738. |> Enum.split_with(fn result -> elem(result, 0) == :error end)
  739. {deleted, updated} =
  740. results
  741. |> Enum.map(fn {:ok, config} ->
  742. Map.put(config, :db, ConfigDB.get_db_keys(config))
  743. end)
  744. |> Enum.split_with(fn config ->
  745. Ecto.get_meta(config, :state) == :deleted
  746. end)
  747. Config.TransferTask.load_and_update_env(deleted, false)
  748. need_reboot? =
  749. Restarter.Pleroma.need_reboot?() ||
  750. Enum.any?(updated, fn config ->
  751. group = ConfigDB.from_string(config.group)
  752. key = ConfigDB.from_string(config.key)
  753. value = ConfigDB.from_binary(config.value)
  754. Config.TransferTask.pleroma_need_restart?(group, key, value)
  755. end)
  756. response = %{configs: updated}
  757. response =
  758. if need_reboot? do
  759. Restarter.Pleroma.need_reboot()
  760. Map.put(response, :need_reboot, need_reboot?)
  761. else
  762. response
  763. end
  764. conn
  765. |> put_view(ConfigView)
  766. |> render("index.json", response)
  767. end
  768. end
  769. def restart(conn, _params) do
  770. with :ok <- configurable_from_database(conn) do
  771. Restarter.Pleroma.restart(Config.get(:env), 50)
  772. json(conn, %{})
  773. end
  774. end
  775. defp configurable_from_database(conn) do
  776. if Config.get(:configurable_from_database) do
  777. :ok
  778. else
  779. errors(
  780. conn,
  781. {:error, "To use this endpoint you need to enable configuration from database."}
  782. )
  783. end
  784. end
  785. def reload_emoji(conn, _params) do
  786. Pleroma.Emoji.reload()
  787. conn |> json("ok")
  788. end
  789. def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  790. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  791. User.toggle_confirmation(users)
  792. ModerationLog.insert_log(%{
  793. actor: admin,
  794. subject: users,
  795. action: "confirm_email"
  796. })
  797. conn |> json("")
  798. end
  799. def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
  800. users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
  801. User.try_send_confirmation_email(users)
  802. ModerationLog.insert_log(%{
  803. actor: admin,
  804. subject: users,
  805. action: "resend_confirmation_email"
  806. })
  807. conn |> json("")
  808. end
  809. def stats(conn, _) do
  810. count = Stats.get_status_visibility_count()
  811. conn
  812. |> json(%{"status_visibility" => count})
  813. end
  814. def errors(conn, {:error, :not_found}) do
  815. conn
  816. |> put_status(:not_found)
  817. |> json(dgettext("errors", "Not found"))
  818. end
  819. def errors(conn, {:error, reason}) do
  820. conn
  821. |> put_status(:bad_request)
  822. |> json(reason)
  823. end
  824. def errors(conn, {:param_cast, _}) do
  825. conn
  826. |> put_status(:bad_request)
  827. |> json(dgettext("errors", "Invalid parameters"))
  828. end
  829. def errors(conn, _) do
  830. conn
  831. |> put_status(:internal_server_error)
  832. |> json(dgettext("errors", "Something went wrong"))
  833. end
  834. defp page_params(params) do
  835. {get_page(params["page"]), get_page_size(params["page_size"])}
  836. end
  837. defp get_page(page_string) when is_nil(page_string), do: 1
  838. defp get_page(page_string) do
  839. case Integer.parse(page_string) do
  840. {page, _} -> page
  841. :error -> 1
  842. end
  843. end
  844. defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
  845. defp get_page_size(page_size_string) do
  846. case Integer.parse(page_size_string) do
  847. {page_size, _} -> page_size
  848. :error -> @users_page_size
  849. end
  850. end
  851. end