MastoAPI: /api/v1/admin/reports
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
48e87be7de
commit
ca5ee8f0bf
7 changed files with 318 additions and 15 deletions
|
@ -40,7 +40,7 @@ def rules_operation do
|
||||||
summary: "Retrieve list of instance rules",
|
summary: "Retrieve list of instance rules",
|
||||||
operationId: "InstanceController.rules",
|
operationId: "InstanceController.rules",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Array of domains", "application/json", array_of_rules())
|
200 => Operation.response("Array of rules", "application/json", array_of_rules())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ def index_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "View accounts by criteria",
|
summary: "View accounts by criteria",
|
||||||
operationId: "Admin.AccountController.index",
|
operationId: "MastodonAdmin.AccountController.index",
|
||||||
description: "View accounts matching certain criteria for filtering, up to 100 at a time.",
|
description: "View accounts matching certain criteria for filtering, up to 100 at a time.",
|
||||||
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -97,7 +97,7 @@ def show_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "View a specific account",
|
summary: "View a specific account",
|
||||||
operationId: "Admin.AccountController.show",
|
operationId: "MastodonAdmin.AccountController.show",
|
||||||
description: "View admin-level information about the given account.",
|
description: "View admin-level information about the given account.",
|
||||||
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
|
@ -115,7 +115,7 @@ def account_action_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Perform an action against an account",
|
summary: "Perform an action against an account",
|
||||||
operationId: "Admin.AccountController.account_action",
|
operationId: "MastodonAdmin.AccountController.account_action",
|
||||||
description:
|
description:
|
||||||
"Perform an action against an account and log this action in the moderation history.",
|
"Perform an action against an account and log this action in the moderation history.",
|
||||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
@ -147,7 +147,7 @@ def delete_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Delete a specific account",
|
summary: "Delete a specific account",
|
||||||
operationId: "Admin.AccountController.delete",
|
operationId: "MastodonAdmin.AccountController.delete",
|
||||||
description: "Delete the given account.",
|
description: "Delete the given account.",
|
||||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
|
@ -165,7 +165,7 @@ def enable_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Re-enable account",
|
summary: "Re-enable account",
|
||||||
operationId: "Admin.AccountController.enable",
|
operationId: "MastodonAdmin.AccountController.enable",
|
||||||
description: "Re-enable a local account whose login is currently disabled.",
|
description: "Re-enable a local account whose login is currently disabled.",
|
||||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
|
@ -183,7 +183,7 @@ def unsensitive_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Unsensitive account",
|
summary: "Unsensitive account",
|
||||||
operationId: "Admin.AccountController.unsensitive",
|
operationId: "MastodonAdmin.AccountController.unsensitive",
|
||||||
description: "Unsensitive a currently sensitized account.",
|
description: "Unsensitive a currently sensitized account.",
|
||||||
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
|
@ -201,7 +201,7 @@ def unsilence_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Unsilence account",
|
summary: "Unsilence account",
|
||||||
operationId: "Admin.AccountController.unsilence",
|
operationId: "MastodonAdmin.AccountController.unsilence",
|
||||||
description: "Unsilence a currently silenced account.",
|
description: "Unsilence a currently silenced account.",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:id, :path, :string, "ID of the account")
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
@ -218,7 +218,7 @@ def unsuspend_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Unsuspend account",
|
summary: "Unsuspend account",
|
||||||
operationId: "Admin.AccountController.unsuspend",
|
operationId: "MastodonAdmin.AccountController.unsuspend",
|
||||||
description: "Unsuspend a currently suspended account.",
|
description: "Unsuspend a currently suspended account.",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:id, :path, :string, "ID of the account")
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
@ -235,7 +235,7 @@ def approve_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Approve pending account",
|
summary: "Approve pending account",
|
||||||
operationId: "Admin.AccountController.approve",
|
operationId: "MastodonAdmin.AccountController.approve",
|
||||||
description: "Approve the given local account if it is currently pending approval.",
|
description: "Approve the given local account if it is currently pending approval.",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:id, :path, :string, "ID of the account")
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
@ -252,7 +252,7 @@ def reject_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["User administration"],
|
tags: ["User administration"],
|
||||||
summary: "Reject pending account",
|
summary: "Reject pending account",
|
||||||
operationId: "Admin.AccountController.reject",
|
operationId: "MastodonAdmin.AccountController.reject",
|
||||||
description: "Reject the given local account if it is currently pending approval.",
|
description: "Reject the given local account if it is currently pending approval.",
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:id, :path, :string, "ID of the account")
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
@ -266,7 +266,7 @@ def reject_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp account do
|
def account do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "AdminAccount",
|
title: "AdminAccount",
|
||||||
description: "Admin-level information about a given account.",
|
description: "Admin-level information about a given account.",
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.MastodonAdmin.ReportOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.MastodonAdmin.AccountOperation
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
|
||||||
|
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: ["Report methods"],
|
||||||
|
summary: "View all reports",
|
||||||
|
operationId: "MastodonAdmin.ReportController.index",
|
||||||
|
description:
|
||||||
|
"View all reports. Pagination may be done with HTTP Link header in the response.",
|
||||||
|
security: [%{"oAuth" => ["admin:read:reports"]}],
|
||||||
|
parameters:
|
||||||
|
[
|
||||||
|
Operation.parameter(:resolved, :query, :boolean, "Filter for resolved reports"),
|
||||||
|
Operation.parameter(:account_id, :query, :string, "Filter by author account id"),
|
||||||
|
Operation.parameter(
|
||||||
|
:target_account_id,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"Filter by report target account id (not implemented)"
|
||||||
|
)
|
||||||
|
] ++
|
||||||
|
pagination_params(),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Account", "application/json", %Schema{
|
||||||
|
title: "ArrayOfReports",
|
||||||
|
type: :array,
|
||||||
|
items: report()
|
||||||
|
}),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Report methods"],
|
||||||
|
summary: "View a single report",
|
||||||
|
operationId: "MastodonAdmin.ReportController.show",
|
||||||
|
description: "View information about the report with the given ID.",
|
||||||
|
security: [%{"oAuth" => ["admin:read:reports"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the report")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", report()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Report methods"],
|
||||||
|
summary: "Mark as resolved",
|
||||||
|
operationId: "MastodonAdmin.ReportController.resolve",
|
||||||
|
description: "Mark a report as resolved with no further action taken.",
|
||||||
|
security: [%{"oAuth" => ["admin:write:reports"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the report")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", report()),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def reopen_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Report methods"],
|
||||||
|
summary: "Re-open report",
|
||||||
|
operationId: "MastodonAdmin.ReportController.reopen",
|
||||||
|
description: "Reopen a currently closed report.",
|
||||||
|
security: [%{"oAuth" => ["admin:write:reports"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the report")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", report()),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report do
|
||||||
|
%Schema{
|
||||||
|
title: "Report",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
action_taken: %Schema{type: :boolean},
|
||||||
|
category: %Schema{type: :string},
|
||||||
|
comment: %Schema{type: :string},
|
||||||
|
created_at: %Schema{type: :string, format: "date-time"},
|
||||||
|
updated_at: %Schema{type: :string, format: "date-time"},
|
||||||
|
account: AccountOperation.account(),
|
||||||
|
target_account: AccountOperation.account(),
|
||||||
|
statuses: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: Status
|
||||||
|
},
|
||||||
|
rules: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
text: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,102 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.ReportController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper,
|
||||||
|
only: [
|
||||||
|
add_link_headers: 2,
|
||||||
|
json_response: 3
|
||||||
|
]
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show])
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["admin:write:reports"]} when action in [:resolve, :reopen])
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MastodonAdmin.ReportOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
opts =
|
||||||
|
%{}
|
||||||
|
|> Map.put(:type, "Flag")
|
||||||
|
|> Map.put(:skip_preload, true)
|
||||||
|
|> Map.put(:preload_report_notes, true)
|
||||||
|
|> Map.put(:total, true)
|
||||||
|
|> restrict_state(params)
|
||||||
|
|> restrict_actor(params)
|
||||||
|
|
||||||
|
# |> restrict_target(params)
|
||||||
|
|
||||||
|
reports =
|
||||||
|
ActivityPub.fetch_activities_query([], opts)
|
||||||
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(reports)
|
||||||
|
|> render("index.json", reports: reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{id: id}) do
|
||||||
|
with %Activity{} = report <- Activity.get_report(id) do
|
||||||
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
|
else
|
||||||
|
_ -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve(conn, %{id: id}) do
|
||||||
|
with {:ok, report} <- CommonAPI.update_report_state(id, "resolved") do
|
||||||
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
|
else
|
||||||
|
{:error, error} ->
|
||||||
|
json_response(conn, :bad_request, %{error: error})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reopen(conn, %{id: id}) do
|
||||||
|
with {:ok, report} <- CommonAPI.update_report_state(id, "open") do
|
||||||
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
|
else
|
||||||
|
{:error, error} ->
|
||||||
|
json_response(conn, :bad_request, %{error: error})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_state(opts, %{resolved: true}), do: Map.put(opts, :state, "resolved")
|
||||||
|
|
||||||
|
defp restrict_state(opts, %{resolved: false}), do: Map.put(opts, :state, "open")
|
||||||
|
|
||||||
|
defp restrict_state(opts, _params), do: opts
|
||||||
|
|
||||||
|
defp restrict_actor(opts, %{account_id: actor}) do
|
||||||
|
with %User{ap_id: ap_id} <- User.get_by_id(actor) do
|
||||||
|
Map.put(opts, :actor_id, ap_id)
|
||||||
|
else
|
||||||
|
_ -> Map.put(opts, :actor_id, actor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_actor(opts, _params), do: opts
|
||||||
|
|
||||||
|
# defp restrict_target(opts, %{target_account_id: target}) do
|
||||||
|
# with %User{id: id} <- User.get_by_ap_id(target) do
|
||||||
|
# Map.put(opts, :user_actor_id, id)
|
||||||
|
# else
|
||||||
|
# _ -> Map.put(opts, :user_actor_id, target)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# defp restrict_target(opts, _params), do: opts
|
||||||
|
end
|
59
lib/pleroma/web/mastodon_api/admin/views/report_view.ex
Normal file
59
lib/pleroma/web/mastodon_api/admin/views/report_view.ex
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.ReportView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
||||||
|
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
def render("index.json", %{reports: reports}) do
|
||||||
|
reports
|
||||||
|
|> Enum.map(&Report.extract_report_info/1)
|
||||||
|
|> Enum.map(&render(__MODULE__, "show.json", &1))
|
||||||
|
|
||||||
|
# |> render_many(__MODULE__, "show.json", as: :report)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{
|
||||||
|
report: report,
|
||||||
|
user: account,
|
||||||
|
account: target_account,
|
||||||
|
statuses: statuses
|
||||||
|
}) do
|
||||||
|
created_at = Utils.to_masto_date(report.data["published"])
|
||||||
|
|
||||||
|
content =
|
||||||
|
unless is_nil(report.data["content"]) do
|
||||||
|
HTML.filter_tags(report.data["content"])
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: report.id,
|
||||||
|
action_taken: report.data["state"] != "open",
|
||||||
|
category: "other",
|
||||||
|
comment: content,
|
||||||
|
created_at: created_at,
|
||||||
|
updated_at: created_at,
|
||||||
|
account: AccountView.render("show.json", %{user: account}),
|
||||||
|
target_account: AccountView.render("show.json", %{user: target_account}),
|
||||||
|
assigned_account: nil,
|
||||||
|
action_taken_by_account: nil,
|
||||||
|
statuses:
|
||||||
|
StatusView.render("index.json", %{
|
||||||
|
activities: statuses,
|
||||||
|
as: :activity
|
||||||
|
}),
|
||||||
|
rules: []
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -313,6 +313,11 @@ defmodule Pleroma.Web.Router do
|
||||||
# post("/accounts/:id/unsuspend", AccountController, :unsuspend)
|
# post("/accounts/:id/unsuspend", AccountController, :unsuspend)
|
||||||
post("/accounts/:id/approve", AccountController, :approve)
|
post("/accounts/:id/approve", AccountController, :approve)
|
||||||
post("/accounts/:id/reject", AccountController, :reject)
|
post("/accounts/:id/reject", AccountController, :reject)
|
||||||
|
|
||||||
|
get("/reports", ReportController, :index)
|
||||||
|
get("/reports/:id", ReportController, :show)
|
||||||
|
post("/reports/:id/resolve", ReportController, :resolve)
|
||||||
|
post("/reports/:id/reopen", ReportController, :reopen)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
|
|
@ -138,9 +138,10 @@ test "reject account", %{conn: conn} do
|
||||||
test "do not allow rejecting already accepted accounts", %{conn: conn} do
|
test "do not allow rejecting already accepted accounts", %{conn: conn} do
|
||||||
%{id: id} = user = insert(:user, is_approved: true)
|
%{id: id} = user = insert(:user, is_approved: true)
|
||||||
|
|
||||||
assert %{"error" => "User is approved"} == conn
|
assert %{"error" => "User is approved"} ==
|
||||||
|> post("/api/v1/admin/accounts/#{id}/reject")
|
conn
|
||||||
|> json_response_and_validate_schema(400)
|
|> post("/api/v1/admin/accounts/#{id}/reject")
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
user = Repo.reload!(user)
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue