forked from AkkomaGang/akkoma
Merge branch 'openapi/admin/config' into 'develop'
Add OpenAPI spec to AdminAPI.ConfigController See merge request pleroma/pleroma!2606
This commit is contained in:
commit
f5cb1f3616
6 changed files with 1588 additions and 1350 deletions
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.ConfigDB
|
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
@ -20,7 +19,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.AdminAPI
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.ConfigView
|
|
||||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
|
@ -28,7 +26,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@descriptions Pleroma.Docs.JSON.compile()
|
|
||||||
@users_page_size 50
|
@users_page_size 50
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -75,11 +72,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], admin: true}
|
%{scopes: ["read"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:config_show,
|
|
||||||
:list_log,
|
:list_log,
|
||||||
:stats,
|
:stats,
|
||||||
:relay_list,
|
:relay_list,
|
||||||
:config_descriptions,
|
|
||||||
:need_reboot
|
:need_reboot
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -89,7 +84,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
%{scopes: ["write"], admin: true}
|
%{scopes: ["write"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:restart,
|
:restart,
|
||||||
:config_update,
|
|
||||||
:resend_confirmation_email,
|
:resend_confirmation_email,
|
||||||
:confirm_email,
|
:confirm_email,
|
||||||
:reload_emoji
|
:reload_emoji
|
||||||
|
@ -645,105 +639,6 @@ def list_log(conn, params) do
|
||||||
|> render("index.json", %{log: log})
|
|> render("index.json", %{log: log})
|
||||||
end
|
end
|
||||||
|
|
||||||
def config_descriptions(conn, _params) do
|
|
||||||
descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
|
|
||||||
|
|
||||||
json(conn, descriptions)
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_show(conn, %{"only_db" => true}) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
configs = Pleroma.Repo.all(ConfigDB)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ConfigView)
|
|
||||||
|> render("index.json", %{configs: configs})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_show(conn, _params) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
configs = ConfigDB.get_all_as_keyword()
|
|
||||||
|
|
||||||
merged =
|
|
||||||
Config.Holder.default_config()
|
|
||||||
|> ConfigDB.merge(configs)
|
|
||||||
|> Enum.map(fn {group, values} ->
|
|
||||||
Enum.map(values, fn {key, value} ->
|
|
||||||
db =
|
|
||||||
if configs[group][key] do
|
|
||||||
ConfigDB.get_db_keys(configs[group][key], key)
|
|
||||||
end
|
|
||||||
|
|
||||||
db_value = configs[group][key]
|
|
||||||
|
|
||||||
merged_value =
|
|
||||||
if !is_nil(db_value) and Keyword.keyword?(db_value) and
|
|
||||||
ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
|
|
||||||
ConfigDB.merge_group(group, key, value, db_value)
|
|
||||||
else
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
setting = %{
|
|
||||||
group: ConfigDB.convert(group),
|
|
||||||
key: ConfigDB.convert(key),
|
|
||||||
value: ConfigDB.convert(merged_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if db, do: Map.put(setting, :db, db), else: setting
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|> List.flatten()
|
|
||||||
|
|
||||||
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_update(conn, %{"configs" => configs}) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
{_errors, results} =
|
|
||||||
configs
|
|
||||||
|> Enum.filter(&whitelisted_config?/1)
|
|
||||||
|> Enum.map(fn
|
|
||||||
%{"group" => group, "key" => key, "delete" => true} = params ->
|
|
||||||
ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
|
|
||||||
|
|
||||||
%{"group" => group, "key" => key, "value" => value} ->
|
|
||||||
ConfigDB.update_or_create(%{group: group, key: key, value: value})
|
|
||||||
end)
|
|
||||||
|> Enum.split_with(fn result -> elem(result, 0) == :error end)
|
|
||||||
|
|
||||||
{deleted, updated} =
|
|
||||||
results
|
|
||||||
|> Enum.map(fn {:ok, config} ->
|
|
||||||
Map.put(config, :db, ConfigDB.get_db_keys(config))
|
|
||||||
end)
|
|
||||||
|> Enum.split_with(fn config ->
|
|
||||||
Ecto.get_meta(config, :state) == :deleted
|
|
||||||
end)
|
|
||||||
|
|
||||||
Config.TransferTask.load_and_update_env(deleted, false)
|
|
||||||
|
|
||||||
if !Restarter.Pleroma.need_reboot?() do
|
|
||||||
changed_reboot_settings? =
|
|
||||||
(updated ++ deleted)
|
|
||||||
|> Enum.any?(fn config ->
|
|
||||||
group = ConfigDB.from_string(config.group)
|
|
||||||
key = ConfigDB.from_string(config.key)
|
|
||||||
value = ConfigDB.from_binary(config.value)
|
|
||||||
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ConfigView)
|
|
||||||
|> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def restart(conn, _params) do
|
def restart(conn, _params) do
|
||||||
with :ok <- configurable_from_database() do
|
with :ok <- configurable_from_database() do
|
||||||
Restarter.Pleroma.restart(Config.get(:env), 50)
|
Restarter.Pleroma.restart(Config.get(:env), 50)
|
||||||
|
@ -764,28 +659,6 @@ defp configurable_from_database do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp whitelisted_config?(group, key) do
|
|
||||||
if whitelisted_configs = Config.get(:database_config_whitelist) do
|
|
||||||
Enum.any?(whitelisted_configs, fn
|
|
||||||
{whitelisted_group} ->
|
|
||||||
group == inspect(whitelisted_group)
|
|
||||||
|
|
||||||
{whitelisted_group, whitelisted_key} ->
|
|
||||||
group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp whitelisted_config?(%{"group" => group, "key" => key}) do
|
|
||||||
whitelisted_config?(group, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp whitelisted_config?(%{:group => group} = config) do
|
|
||||||
whitelisted_config?(group, config[:key])
|
|
||||||
end
|
|
||||||
|
|
||||||
def reload_emoji(conn, _params) do
|
def reload_emoji(conn, _params) do
|
||||||
Pleroma.Emoji.reload()
|
Pleroma.Emoji.reload()
|
||||||
|
|
||||||
|
|
153
lib/pleroma/web/admin_api/controllers/config_controller.ex
Normal file
153
lib/pleroma/web/admin_api/controllers/config_controller.ex
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ConfigController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ConfigDB
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
@descriptions Pleroma.Docs.JSON.compile()
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read"], admin: true}
|
||||||
|
when action in [:show, :descriptions]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
|
||||||
|
|
||||||
|
def descriptions(conn, _params) do
|
||||||
|
descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
|
||||||
|
|
||||||
|
json(conn, descriptions)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{only_db: true}) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
configs = Pleroma.Repo.all(ConfigDB)
|
||||||
|
render(conn, "index.json", %{configs: configs})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
configs = ConfigDB.get_all_as_keyword()
|
||||||
|
|
||||||
|
merged =
|
||||||
|
Config.Holder.default_config()
|
||||||
|
|> ConfigDB.merge(configs)
|
||||||
|
|> Enum.map(fn {group, values} ->
|
||||||
|
Enum.map(values, fn {key, value} ->
|
||||||
|
db =
|
||||||
|
if configs[group][key] do
|
||||||
|
ConfigDB.get_db_keys(configs[group][key], key)
|
||||||
|
end
|
||||||
|
|
||||||
|
db_value = configs[group][key]
|
||||||
|
|
||||||
|
merged_value =
|
||||||
|
if not is_nil(db_value) and Keyword.keyword?(db_value) and
|
||||||
|
ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
|
||||||
|
ConfigDB.merge_group(group, key, value, db_value)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
setting = %{
|
||||||
|
group: ConfigDB.convert(group),
|
||||||
|
key: ConfigDB.convert(key),
|
||||||
|
value: ConfigDB.convert(merged_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if db, do: Map.put(setting, :db, db), else: setting
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> List.flatten()
|
||||||
|
|
||||||
|
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{body_params: %{configs: configs}} = conn, _) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
results =
|
||||||
|
configs
|
||||||
|
|> Enum.filter(&whitelisted_config?/1)
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{group: group, key: key, delete: true} = params ->
|
||||||
|
ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]})
|
||||||
|
|
||||||
|
%{group: group, key: key, value: value} ->
|
||||||
|
ConfigDB.update_or_create(%{group: group, key: key, value: value})
|
||||||
|
end)
|
||||||
|
|> Enum.reject(fn {result, _} -> result == :error end)
|
||||||
|
|
||||||
|
{deleted, updated} =
|
||||||
|
results
|
||||||
|
|> Enum.map(fn {:ok, config} ->
|
||||||
|
Map.put(config, :db, ConfigDB.get_db_keys(config))
|
||||||
|
end)
|
||||||
|
|> Enum.split_with(fn config ->
|
||||||
|
Ecto.get_meta(config, :state) == :deleted
|
||||||
|
end)
|
||||||
|
|
||||||
|
Config.TransferTask.load_and_update_env(deleted, false)
|
||||||
|
|
||||||
|
if not Restarter.Pleroma.need_reboot?() do
|
||||||
|
changed_reboot_settings? =
|
||||||
|
(updated ++ deleted)
|
||||||
|
|> Enum.any?(fn config ->
|
||||||
|
group = ConfigDB.from_string(config.group)
|
||||||
|
key = ConfigDB.from_string(config.key)
|
||||||
|
value = ConfigDB.from_binary(config.value)
|
||||||
|
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
||||||
|
end
|
||||||
|
|
||||||
|
render(conn, "index.json", %{
|
||||||
|
configs: updated,
|
||||||
|
need_reboot: Restarter.Pleroma.need_reboot?()
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp configurable_from_database do
|
||||||
|
if Config.get(:configurable_from_database) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, "To use this endpoint you need to enable configuration from database."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(group, key) do
|
||||||
|
if whitelisted_configs = Config.get(:database_config_whitelist) do
|
||||||
|
Enum.any?(whitelisted_configs, fn
|
||||||
|
{whitelisted_group} ->
|
||||||
|
group == inspect(whitelisted_group)
|
||||||
|
|
||||||
|
{whitelisted_group, whitelisted_key} ->
|
||||||
|
group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(%{group: group, key: key}) do
|
||||||
|
whitelisted_config?(group, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(%{group: group} = config) do
|
||||||
|
whitelisted_config?(group, config[:key])
|
||||||
|
end
|
||||||
|
end
|
142
lib/pleroma/web/api_spec/operations/admin/config_operation.ex
Normal file
142
lib/pleroma/web/api_spec/operations/admin/config_operation.ex
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Get list of merged default settings with saved in database",
|
||||||
|
operationId: "AdminAPI.ConfigController.show",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:only_db,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Get only saved in database settings"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Config", "application/json", config_response()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Update config settings",
|
||||||
|
operationId: "AdminAPI.ConfigController.update",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
configs: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
value: any(),
|
||||||
|
delete: %Schema{type: :boolean},
|
||||||
|
subkeys: %Schema{type: :array, items: %Schema{type: :string}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Config", "application/json", config_response()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def descriptions_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Get JSON with config descriptions.",
|
||||||
|
operationId: "AdminAPI.ConfigController.descriptions",
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Config Descriptions", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
children: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
suggestions: %Schema{type: :array}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp any do
|
||||||
|
%Schema{
|
||||||
|
oneOf: [
|
||||||
|
%Schema{type: :array},
|
||||||
|
%Schema{type: :object},
|
||||||
|
%Schema{type: :string},
|
||||||
|
%Schema{type: :integer},
|
||||||
|
%Schema{type: :boolean}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp config_response do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
configs: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
value: any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
need_reboot: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -194,9 +194,9 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/statuses/:id", StatusController, :delete)
|
delete("/statuses/:id", StatusController, :delete)
|
||||||
get("/statuses", StatusController, :index)
|
get("/statuses", StatusController, :index)
|
||||||
|
|
||||||
get("/config", AdminAPIController, :config_show)
|
get("/config", ConfigController, :show)
|
||||||
post("/config", AdminAPIController, :config_update)
|
post("/config", ConfigController, :update)
|
||||||
get("/config/descriptions", AdminAPIController, :config_descriptions)
|
get("/config/descriptions", ConfigController, :descriptions)
|
||||||
get("/need_reboot", AdminAPIController, :need_reboot)
|
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||||
get("/restart", AdminAPIController, :restart)
|
get("/restart", AdminAPIController, :restart)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1290
test/web/admin_api/controllers/config_controller_test.exs
Normal file
1290
test/web/admin_api/controllers/config_controller_test.exs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue