174 lines
5.5 KiB
Erlang
174 lines
5.5 KiB
Erlang
%%%----------------------------------------------------------------------
|
|
%%% File : mod_webadmin_config.erl
|
|
%%% Author : Badlop <badlop@process-one.net>
|
|
%%% Purpose : Show Configuration in Web Admin
|
|
%%% Created : 5 Aug 2022 by Badlop <badlop@process-one.net>
|
|
%%%----------------------------------------------------------------------
|
|
|
|
-module(mod_webadmin_config).
|
|
-author('badlop@process-one.net').
|
|
|
|
-behaviour(gen_mod).
|
|
|
|
-export([start/2, stop/1, depends/2, mod_options/1, mod_doc/0, mod_status/0]).
|
|
-export([get_content/3]).
|
|
-export([web_menu_node/3, web_page_node/5]).
|
|
|
|
-include_lib("xmpp/include/xmpp.hrl").
|
|
-include("ejabberd_http.hrl").
|
|
-include("ejabberd_web_admin.hrl").
|
|
-include("logger.hrl").
|
|
-include("translate.hrl").
|
|
|
|
-define(OLDFILE, "old.yml").
|
|
-define(NEWFILE, "new.yml").
|
|
|
|
%%-------------------
|
|
%% gen_mod functions
|
|
%%-------------------
|
|
|
|
start(_Host, _Opts) ->
|
|
ejabberd_hooks:add(webadmin_menu_node, ?MODULE, web_menu_node, 50),
|
|
ejabberd_hooks:add(webadmin_page_node, ?MODULE, web_page_node, 50),
|
|
ok.
|
|
|
|
stop(_Host) ->
|
|
ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, web_menu_node, 50),
|
|
ejabberd_hooks:delete(webadmin_page_node, ?MODULE, web_page_node, 50),
|
|
ok.
|
|
|
|
depends(_Host, _Opts) ->
|
|
[].
|
|
|
|
mod_options(_Host) ->
|
|
[].
|
|
|
|
mod_doc() -> #{}.
|
|
|
|
mod_status() ->
|
|
"Page available in WebAdmin -> Nodes -> your node -> Configuration".
|
|
|
|
%%-------------------
|
|
%% Web Admin Menu
|
|
%%-------------------
|
|
|
|
web_menu_node(Acc, _Node, Lang) ->
|
|
Acc ++ [{<<"config">>, translate:translate(Lang, ?T("Configuration"))}].
|
|
|
|
%%-------------------
|
|
%% Web Admin Page
|
|
%%-------------------
|
|
|
|
web_page_node(_, Node, [<<"config">>], Query, Lang) ->
|
|
Res = rpc:call(Node, mod_webadmin_config, get_content, [Node, Query, Lang]),
|
|
{stop, Res};
|
|
web_page_node(Acc, _, _, _, _) ->
|
|
Acc.
|
|
|
|
%%-------------------
|
|
%% Config Changes
|
|
%%-------------------
|
|
|
|
config_tmp_filename(Filename) ->
|
|
filename:join(filename:dirname(ejabberd_logger:get_log_path()),
|
|
Filename).
|
|
|
|
dump_config(Filename) ->
|
|
ejabberd_config:dump(config_tmp_filename(Filename)).
|
|
|
|
get_last_config_changes() ->
|
|
OldFn = config_tmp_filename(?OLDFILE),
|
|
NewFn = config_tmp_filename(?NEWFILE),
|
|
case {file:read_file(OldFn), file:read_file(NewFn)} of
|
|
{{ok, _}, {ok, _}} ->
|
|
os:cmd("diff -u " ++
|
|
config_tmp_filename(?OLDFILE) ++
|
|
" " ++
|
|
config_tmp_filename(?NEWFILE));
|
|
_ ->
|
|
"Old and/or new configuration not found."
|
|
end.
|
|
|
|
get_lines_length(Data) ->
|
|
[LongString] = io_lib:format("~s", [lists:flatten(Data)]),
|
|
%% The newline character \n is integer 10 in erlang
|
|
lists:foldl(fun (10, Sum) -> Sum + 1;
|
|
(_, Sum) -> Sum
|
|
end,
|
|
0,
|
|
LongString
|
|
).
|
|
|
|
get_config_paths_els() ->
|
|
Bins = [ejabberd_config:path(),
|
|
ext_mod:config_dir()
|
|
| ext_mod:modules_configs()],
|
|
[?LI([?C(Bin)]) || Bin <- Bins].
|
|
|
|
%%-------------------
|
|
%% Generate content
|
|
%%-------------------
|
|
|
|
get_content(Node, Query, Lang) ->
|
|
Res = node_reload_parse_query(Node, Query, Lang),
|
|
Y = ejabberd_config:get_option(yaml_config),
|
|
Data = fast_yaml:encode(Y),
|
|
LastConfigChanges = get_last_config_changes(),
|
|
ResEl = case Res of
|
|
ok -> [?XREST(?T("Submitted"))];
|
|
{error, ErrorText} -> [?XREST(<<"Error: ", ErrorText/binary>>)];
|
|
nothing -> []
|
|
end,
|
|
ConfigurationPathsEls = get_config_paths_els(),
|
|
?H1GL(str:translate_and_format(Lang, ?T("Configuration at ~p"), [Node]),
|
|
<<"file-format/#reload-at-runtime">>,
|
|
<<"Reload at Runtime">>) ++
|
|
ResEl ++
|
|
[?XCT(<<"h3">>, ?T("Configuration paths:")),
|
|
?XE(<<"ul">>, ConfigurationPathsEls),
|
|
?BR,
|
|
?XAE(<<"form">>,
|
|
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
|
[?INPUTT(<<"submit">>, <<"reload">>, ?T("Reload Configuration"))]
|
|
),
|
|
?XCT(<<"h3">>, ?T("Configuration changes in last reload:")),
|
|
?XAC(<<"textarea">>,
|
|
[{<<"wrap">>, <<"off">>},
|
|
{<<"style">>, <<"font-family:monospace;">>},
|
|
{<<"class">>, <<"result">>},
|
|
{<<"name">>, <<"result">>},
|
|
{<<"rows">>, integer_to_binary(get_lines_length(LastConfigChanges))},
|
|
{<<"cols">>, <<"80">>}],
|
|
list_to_binary(LastConfigChanges)
|
|
),
|
|
?XCT(<<"h3">>, ?T("Configuration in memory:")),
|
|
?XAC(<<"textarea">>,
|
|
[{<<"wrap">>, <<"off">>},
|
|
{<<"style">>, <<"font-family:monospace;">>},
|
|
{<<"class">>, <<"result">>},
|
|
{<<"name">>, <<"result">>},
|
|
{<<"rows">>, integer_to_binary(get_lines_length(Data))},
|
|
{<<"cols">>, <<"80">>}],
|
|
list_to_binary(Data)
|
|
)
|
|
].
|
|
|
|
node_reload_parse_query(Node, Query, _Lang) ->
|
|
case lists:keysearch(<<"reload">>, 1, Query) of
|
|
{value, _} ->
|
|
dump_config(?OLDFILE),
|
|
case ejabberd_cluster:call(Node, ejabberd_config, reload, []) of
|
|
ok ->
|
|
dump_config(?NEWFILE),
|
|
ok;
|
|
{error, Error} ->
|
|
?ERROR_MSG("~p~n", [Error]),
|
|
{error, (str:format("~p", [Error]))};
|
|
{badrpc, Error} ->
|
|
?ERROR_MSG("Bad RPC: ~p~n", [Error]),
|
|
{error, <<"Bad RPC: ", (str:format("~p", [Error]))/binary>>}
|
|
end;
|
|
_ ->
|
|
nothing
|
|
end.
|