chore: dashboard format code
This commit is contained in:
parent
2ea66ebcee
commit
07444e3da5
|
@ -16,22 +16,23 @@
|
|||
-define(ADMIN, emqx_admin).
|
||||
|
||||
-record(?ADMIN, {
|
||||
username :: binary(),
|
||||
pwdhash :: binary(),
|
||||
description :: binary(),
|
||||
username :: binary(),
|
||||
pwdhash :: binary(),
|
||||
description :: binary(),
|
||||
role = undefined :: atom(),
|
||||
extra = [] :: term() %% not used so far, for future extension
|
||||
}).
|
||||
|
||||
%% not used so far, for future extension
|
||||
extra = [] :: term()
|
||||
}).
|
||||
|
||||
-define(ADMIN_JWT, emqx_admin_jwt).
|
||||
|
||||
-record(?ADMIN_JWT, {
|
||||
token :: binary(),
|
||||
username :: binary(),
|
||||
exptime :: integer(),
|
||||
extra = [] :: term() %% not used so far, fur future extension
|
||||
}).
|
||||
token :: binary(),
|
||||
username :: binary(),
|
||||
exptime :: integer(),
|
||||
%% not used so far, fur future extension
|
||||
extra = [] :: term()
|
||||
}).
|
||||
|
||||
-define(TAB_COLLECT, emqx_collect).
|
||||
|
||||
|
@ -49,27 +50,27 @@
|
|||
-define(RPC_TIMEOUT, 5000).
|
||||
-endif.
|
||||
|
||||
-define(DELTA_SAMPLER_LIST,
|
||||
[ received
|
||||
-define(DELTA_SAMPLER_LIST, [
|
||||
received,
|
||||
%, received_bytes
|
||||
, sent
|
||||
sent,
|
||||
%, sent_bytes
|
||||
, dropped
|
||||
]).
|
||||
dropped
|
||||
]).
|
||||
|
||||
-define(GAUGE_SAMPLER_LIST,
|
||||
[ subscriptions
|
||||
, topics
|
||||
, connections
|
||||
]).
|
||||
-define(GAUGE_SAMPLER_LIST, [
|
||||
subscriptions,
|
||||
topics,
|
||||
connections
|
||||
]).
|
||||
|
||||
-define(SAMPLER_LIST, ?GAUGE_SAMPLER_LIST ++ ?DELTA_SAMPLER_LIST).
|
||||
|
||||
-define(DELTA_SAMPLER_RATE_MAP, #{
|
||||
received => received_msg_rate,
|
||||
received => received_msg_rate,
|
||||
%% In 5.0.0, temporarily comment it to suppress bytes rate
|
||||
%received_bytes => received_bytes_rate,
|
||||
%sent_bytes => sent_bytes_rate,
|
||||
sent => sent_msg_rate,
|
||||
dropped => dropped_msg_rate
|
||||
}).
|
||||
sent => sent_msg_rate,
|
||||
dropped => dropped_msg_rate
|
||||
}).
|
||||
|
|
|
@ -3,16 +3,25 @@
|
|||
{deps, [{emqx, {path, "../emqx"}}]}.
|
||||
|
||||
{edoc_opts, [{preprocess, true}]}.
|
||||
{erl_opts, [warn_unused_vars,
|
||||
warn_shadow_vars,
|
||||
warn_unused_import,
|
||||
warn_obsolete_guard,
|
||||
debug_info,
|
||||
{d, 'APPLICATION', emqx}]}.
|
||||
{xref_checks, [undefined_function_calls, undefined_functions,
|
||||
locals_not_used, deprecated_function_calls,
|
||||
warnings_as_errors, deprecated_functions]}.
|
||||
{erl_opts, [
|
||||
warn_unused_vars,
|
||||
warn_shadow_vars,
|
||||
warn_unused_import,
|
||||
warn_obsolete_guard,
|
||||
debug_info,
|
||||
{d, 'APPLICATION', emqx}
|
||||
]}.
|
||||
{xref_checks, [
|
||||
undefined_function_calls,
|
||||
undefined_functions,
|
||||
locals_not_used,
|
||||
deprecated_function_calls,
|
||||
warnings_as_errors,
|
||||
deprecated_functions
|
||||
]}.
|
||||
{cover_enabled, true}.
|
||||
{cover_opts, [verbose]}.
|
||||
{cover_export_enabled, true}.
|
||||
{eunit_first_files, ["test/emqx_swagger_remote_schema.erl"]}.
|
||||
|
||||
{project_plugins, [erlfmt]}.
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
%% -*- mode: erlang -*-
|
||||
{application, emqx_dashboard,
|
||||
[{description, "EMQX Web Dashboard"},
|
||||
{vsn, "5.0.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_dashboard_sup]},
|
||||
{applications, [kernel,stdlib,mnesia,minirest,emqx]},
|
||||
{mod, {emqx_dashboard_app,[]}},
|
||||
{env, []},
|
||||
{licenses, ["Apache-2.0"]},
|
||||
{maintainers, ["EMQX Team <contact@emqx.io>"]},
|
||||
{links, [{"Homepage", "https://emqx.io/"},
|
||||
{"Github", "https://github.com/emqx/emqx-dashboard"}
|
||||
]}
|
||||
]}.
|
||||
{application, emqx_dashboard, [
|
||||
{description, "EMQX Web Dashboard"},
|
||||
% strict semver, bump manually!
|
||||
{vsn, "5.0.0"},
|
||||
{modules, []},
|
||||
{registered, [emqx_dashboard_sup]},
|
||||
{applications, [kernel, stdlib, mnesia, minirest, emqx]},
|
||||
{mod, {emqx_dashboard_app, []}},
|
||||
{env, []},
|
||||
{licenses, ["Apache-2.0"]},
|
||||
{maintainers, ["EMQX Team <contact@emqx.io>"]},
|
||||
{links, [
|
||||
{"Homepage", "https://emqx.io/"},
|
||||
{"Github", "https://github.com/emqx/emqx-dashboard"}
|
||||
]}
|
||||
]}.
|
||||
|
|
|
@ -26,28 +26,32 @@
|
|||
%% Mnesia bootstrap
|
||||
-export([mnesia/1]).
|
||||
|
||||
-export([ add_user/3
|
||||
, force_add_user/3
|
||||
, remove_user/1
|
||||
, update_user/2
|
||||
, lookup_user/1
|
||||
, change_password/2
|
||||
, change_password/3
|
||||
, all_users/0
|
||||
, check/2
|
||||
]).
|
||||
-export([
|
||||
add_user/3,
|
||||
force_add_user/3,
|
||||
remove_user/1,
|
||||
update_user/2,
|
||||
lookup_user/1,
|
||||
change_password/2,
|
||||
change_password/3,
|
||||
all_users/0,
|
||||
check/2
|
||||
]).
|
||||
|
||||
-export([ sign_token/2
|
||||
, verify_token/1
|
||||
, destroy_token_by_username/2
|
||||
]).
|
||||
-export([ hash/1
|
||||
, verify_hash/2
|
||||
]).
|
||||
-export([
|
||||
sign_token/2,
|
||||
verify_token/1,
|
||||
destroy_token_by_username/2
|
||||
]).
|
||||
-export([
|
||||
hash/1,
|
||||
verify_hash/2
|
||||
]).
|
||||
|
||||
-export([ add_default_user/0
|
||||
, default_username/0
|
||||
]).
|
||||
-export([
|
||||
add_default_user/0,
|
||||
default_username/0
|
||||
]).
|
||||
|
||||
-type emqx_admin() :: #?ADMIN{}.
|
||||
|
||||
|
@ -57,42 +61,52 @@
|
|||
|
||||
mnesia(boot) ->
|
||||
ok = mria:create_table(?ADMIN, [
|
||||
{type, set},
|
||||
{rlog_shard, ?DASHBOARD_SHARD},
|
||||
{storage, disc_copies},
|
||||
{record_name, ?ADMIN},
|
||||
{attributes, record_info(fields, ?ADMIN)},
|
||||
{storage_properties, [{ets, [{read_concurrency, true},
|
||||
{write_concurrency, true}]}]}]).
|
||||
{type, set},
|
||||
{rlog_shard, ?DASHBOARD_SHARD},
|
||||
{storage, disc_copies},
|
||||
{record_name, ?ADMIN},
|
||||
{attributes, record_info(fields, ?ADMIN)},
|
||||
{storage_properties, [
|
||||
{ets, [
|
||||
{read_concurrency, true},
|
||||
{write_concurrency, true}
|
||||
]}
|
||||
]}
|
||||
]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(add_user(binary(), binary(), binary()) -> {ok, map()} | {error, any()}).
|
||||
add_user(Username, Password, Desc)
|
||||
when is_binary(Username), is_binary(Password) ->
|
||||
-spec add_user(binary(), binary(), binary()) -> {ok, map()} | {error, any()}.
|
||||
add_user(Username, Password, Desc) when
|
||||
is_binary(Username), is_binary(Password)
|
||||
->
|
||||
case legal_username(Username) of
|
||||
true ->
|
||||
return(
|
||||
mria:transaction(?DASHBOARD_SHARD, fun add_user_/3, [Username, Password, Desc]));
|
||||
mria:transaction(?DASHBOARD_SHARD, fun add_user_/3, [Username, Password, Desc])
|
||||
);
|
||||
false ->
|
||||
{error, <<"Bad Username."
|
||||
" Only upper and lower case letters, numbers and underscores are supported">>}
|
||||
{error, <<
|
||||
"Bad Username."
|
||||
" Only upper and lower case letters, numbers and underscores are supported"
|
||||
>>}
|
||||
end.
|
||||
|
||||
%% 0 - 9 or A -Z or a - z or $_
|
||||
legal_username(<<>>) -> false;
|
||||
legal_username(UserName) ->
|
||||
nomatch /= re:run(UserName, "^[_a-zA-Z0-9]*$").
|
||||
legal_username(UserName) -> nomatch /= re:run(UserName, "^[_a-zA-Z0-9]*$").
|
||||
|
||||
%% black-magic: force overwrite a user
|
||||
force_add_user(Username, Password, Desc) ->
|
||||
AddFun = fun() ->
|
||||
mnesia:write(#?ADMIN{username = Username,
|
||||
pwdhash = hash(Password),
|
||||
description = Desc})
|
||||
end,
|
||||
mnesia:write(#?ADMIN{
|
||||
username = Username,
|
||||
pwdhash = hash(Password),
|
||||
description = Desc
|
||||
})
|
||||
end,
|
||||
case mria:transaction(?DASHBOARD_SHARD, AddFun) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Reason} -> {error, Reason}
|
||||
|
@ -101,7 +115,7 @@ force_add_user(Username, Password, Desc) ->
|
|||
%% @private
|
||||
add_user_(Username, Password, Desc) ->
|
||||
case mnesia:wread({?ADMIN, Username}) of
|
||||
[] ->
|
||||
[] ->
|
||||
Admin = #?ADMIN{username = Username, pwdhash = hash(Password), description = Desc},
|
||||
mnesia:write(Admin),
|
||||
#{username => Username, description => Desc};
|
||||
|
@ -109,14 +123,14 @@ add_user_(Username, Password, Desc) ->
|
|||
mnesia:abort(<<"username_already_exist">>)
|
||||
end.
|
||||
|
||||
-spec(remove_user(binary()) -> {ok, any()} | {error, any()}).
|
||||
-spec remove_user(binary()) -> {ok, any()} | {error, any()}.
|
||||
remove_user(Username) when is_binary(Username) ->
|
||||
Trans = fun() ->
|
||||
case lookup_user(Username) of
|
||||
[] -> mnesia:abort(<<"username_not_found">>);
|
||||
_ -> mnesia:delete({?ADMIN, Username})
|
||||
end
|
||||
end,
|
||||
case lookup_user(Username) of
|
||||
[] -> mnesia:abort(<<"username_not_found">>);
|
||||
_ -> mnesia:delete({?ADMIN, Username})
|
||||
end
|
||||
end,
|
||||
case return(mria:transaction(?DASHBOARD_SHARD, Trans)) of
|
||||
{ok, Result} ->
|
||||
_ = emqx_dashboard_token:destroy_by_username(Username),
|
||||
|
@ -125,7 +139,7 @@ remove_user(Username) when is_binary(Username) ->
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec(update_user(binary(), binary()) -> {ok, map()} | {error, term()}).
|
||||
-spec update_user(binary(), binary()) -> {ok, map()} | {error, term()}.
|
||||
update_user(Username, Desc) when is_binary(Username) ->
|
||||
return(mria:transaction(?DASHBOARD_SHARD, fun update_user_/2, [Username, Desc])).
|
||||
|
||||
|
@ -140,7 +154,8 @@ verify_hash(Origin, SaltHash) ->
|
|||
true -> ok;
|
||||
false -> error
|
||||
end;
|
||||
_ -> error
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
sha256(SaltBin, Password) ->
|
||||
|
@ -174,7 +189,8 @@ change_password_hash(Username, PasswordHash) ->
|
|||
{ok, Result} ->
|
||||
_ = emqx_dashboard_token:destroy_by_username(Username),
|
||||
{ok, Result};
|
||||
{error, Reason} -> {error, Reason}
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
update_pwd(Username, Fun) ->
|
||||
|
@ -183,30 +199,35 @@ update_pwd(Username, Fun) ->
|
|||
User =
|
||||
case lookup_user(Username) of
|
||||
[Admin] -> Admin;
|
||||
[] ->
|
||||
mnesia:abort(<<"username_not_found">>)
|
||||
[] -> mnesia:abort(<<"username_not_found">>)
|
||||
end,
|
||||
mnesia:write(Fun(User))
|
||||
end,
|
||||
return(mria:transaction(?DASHBOARD_SHARD, Trans)).
|
||||
|
||||
|
||||
-spec(lookup_user(binary()) -> [emqx_admin()]).
|
||||
-spec lookup_user(binary()) -> [emqx_admin()].
|
||||
lookup_user(Username) when is_binary(Username) ->
|
||||
Fun = fun() -> mnesia:read(?ADMIN, Username) end,
|
||||
{atomic, User} = mria:ro_transaction(?DASHBOARD_SHARD, Fun),
|
||||
User.
|
||||
|
||||
-spec(all_users() -> [map()]).
|
||||
-spec all_users() -> [map()].
|
||||
all_users() ->
|
||||
lists:map(fun(#?ADMIN{username = Username,
|
||||
description = Desc
|
||||
}) ->
|
||||
#{username => Username,
|
||||
description => Desc
|
||||
}
|
||||
end, ets:tab2list(?ADMIN)).
|
||||
-spec(return({atomic | aborted, term()}) -> {ok, term()} | {error, Reason :: binary()}).
|
||||
lists:map(
|
||||
fun(
|
||||
#?ADMIN{
|
||||
username = Username,
|
||||
description = Desc
|
||||
}
|
||||
) ->
|
||||
#{
|
||||
username => Username,
|
||||
description => Desc
|
||||
}
|
||||
end,
|
||||
ets:tab2list(?ADMIN)
|
||||
).
|
||||
-spec return({atomic | aborted, term()}) -> {ok, term()} | {error, Reason :: binary()}.
|
||||
return({atomic, Result}) ->
|
||||
{ok, Result};
|
||||
return({aborted, Reason}) ->
|
||||
|
@ -220,7 +241,7 @@ check(Username, Password) ->
|
|||
case lookup_user(Username) of
|
||||
[#?ADMIN{pwdhash = PwdHash}] ->
|
||||
case verify_hash(Password, PwdHash) of
|
||||
ok -> ok;
|
||||
ok -> ok;
|
||||
error -> {error, <<"password_error">>}
|
||||
end;
|
||||
[] ->
|
||||
|
@ -252,7 +273,7 @@ destroy_token_by_username(Username, Token) ->
|
|||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(add_default_user() -> {ok, map() | empty | default_user_exists } | {error, any()}).
|
||||
-spec add_default_user() -> {ok, map() | empty | default_user_exists} | {error, any()}.
|
||||
add_default_user() ->
|
||||
add_default_user(binenv(default_username), binenv(default_password)).
|
||||
|
||||
|
@ -264,9 +285,8 @@ binenv(Key) ->
|
|||
|
||||
add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) ->
|
||||
{ok, empty};
|
||||
|
||||
add_default_user(Username, Password) ->
|
||||
case lookup_user(Username) of
|
||||
[] -> add_user(Username, Password, <<"administrator">>);
|
||||
_ -> {ok, default_user_exists}
|
||||
_ -> {ok, default_user_exists}
|
||||
end.
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
mk/2,
|
||||
array/1,
|
||||
enum/1
|
||||
]).
|
||||
]).
|
||||
|
||||
-export([
|
||||
api_spec/0,
|
||||
|
@ -35,7 +35,7 @@
|
|||
paths/0,
|
||||
schema/1,
|
||||
namespace/0
|
||||
]).
|
||||
]).
|
||||
|
||||
-export([
|
||||
login/2,
|
||||
|
@ -43,7 +43,7 @@
|
|||
users/2,
|
||||
user/2,
|
||||
change_pwd/2
|
||||
]).
|
||||
]).
|
||||
|
||||
-define(EMPTY(V), (V == undefined orelse V == <<>>)).
|
||||
|
||||
|
@ -60,11 +60,13 @@ api_spec() ->
|
|||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}).
|
||||
|
||||
paths() ->
|
||||
[ "/login"
|
||||
, "/logout"
|
||||
, "/users"
|
||||
, "/users/:username"
|
||||
, "/users/:username/change_pwd"].
|
||||
[
|
||||
"/login",
|
||||
"/logout",
|
||||
"/users",
|
||||
"/users/:username",
|
||||
"/users/:username/change_pwd"
|
||||
].
|
||||
|
||||
schema("/login") ->
|
||||
#{
|
||||
|
@ -101,8 +103,10 @@ schema("/users") ->
|
|||
tags => [<<"dashboard">>],
|
||||
desc => ?DESC(list_users_api),
|
||||
responses => #{
|
||||
200 => mk(array(hoconsc:ref(user)),
|
||||
#{desc => ?DESC(list_users_api)})
|
||||
200 => mk(
|
||||
array(hoconsc:ref(user)),
|
||||
#{desc => ?DESC(list_users_api)}
|
||||
)
|
||||
}
|
||||
},
|
||||
post => #{
|
||||
|
@ -114,7 +118,6 @@ schema("/users") ->
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
schema("/users/:username") ->
|
||||
#{
|
||||
'operationId' => user,
|
||||
|
@ -135,7 +138,8 @@ schema("/users/:username") ->
|
|||
responses => #{
|
||||
204 => <<"Delete User successfully">>,
|
||||
400 => emqx_dashboard_swagger:error_codes(
|
||||
[?BAD_REQUEST, ?NOT_ALLOWED], ?DESC(login_failed_response400)),
|
||||
[?BAD_REQUEST, ?NOT_ALLOWED], ?DESC(login_failed_response400)
|
||||
),
|
||||
404 => response_schema(404)
|
||||
}
|
||||
}
|
||||
|
@ -151,10 +155,12 @@ schema("/users/:username/change_pwd") ->
|
|||
responses => #{
|
||||
204 => <<"Update user password successfully">>,
|
||||
401 => emqx_dashboard_swagger:error_codes(
|
||||
[?WRONG_USERNAME_OR_PWD, ?ERROR_PWD_NOT_MATCH], ?DESC(login_failed401)),
|
||||
[?WRONG_USERNAME_OR_PWD, ?ERROR_PWD_NOT_MATCH], ?DESC(login_failed401)
|
||||
),
|
||||
404 => response_schema(404),
|
||||
400 => emqx_dashboard_swagger:error_codes(
|
||||
[?BAD_REQUEST], ?DESC(login_failed_response400))
|
||||
[?BAD_REQUEST], ?DESC(login_failed_response400)
|
||||
)
|
||||
}
|
||||
}
|
||||
}.
|
||||
|
@ -174,26 +180,32 @@ field(username) ->
|
|||
mk(binary(), #{desc => ?DESC(username), 'maxLength' => 100, example => <<"admin">>})};
|
||||
field(username_in_path) ->
|
||||
{username,
|
||||
mk(binary(), #{desc => ?DESC(username), 'maxLength' => 100, example => <<"admin">>,
|
||||
in => path, required => true})};
|
||||
mk(binary(), #{
|
||||
desc => ?DESC(username),
|
||||
'maxLength' => 100,
|
||||
example => <<"admin">>,
|
||||
in => path,
|
||||
required => true
|
||||
})};
|
||||
field(password) ->
|
||||
{password,
|
||||
mk(binary(), #{desc => ?DESC(password), 'maxLength' => 100, example => <<"public">>})};
|
||||
field(description) ->
|
||||
{description,
|
||||
mk(binary(), #{desc => ?DESC(user_description), example => <<"administrator">>})};
|
||||
{description, mk(binary(), #{desc => ?DESC(user_description), example => <<"administrator">>})};
|
||||
field(token) ->
|
||||
{token, mk(binary(), #{desc => ?DESC(token)})};
|
||||
field(license) ->
|
||||
{license, [
|
||||
{edition, mk(enum([community, enterprise]),
|
||||
#{desc => ?DESC(license), example => community})}]};
|
||||
{edition,
|
||||
mk(
|
||||
enum([community, enterprise]),
|
||||
#{desc => ?DESC(license), example => community}
|
||||
)}
|
||||
]};
|
||||
field(version) ->
|
||||
{version, mk(string(), #{desc => ?DESC(version), example => <<"5.0.0">>})};
|
||||
|
||||
field(old_pwd) ->
|
||||
{old_pwd, mk(binary(), #{desc => ?DESC(old_pwd)})};
|
||||
|
||||
field(new_pwd) ->
|
||||
{new_pwd, mk(binary(), #{desc => ?DESC(new_pwd)})}.
|
||||
|
||||
|
@ -207,17 +219,20 @@ login(post, #{body := Params}) ->
|
|||
{ok, Token} ->
|
||||
?SLOG(info, #{msg => "Dashboard login successfully", username => Username}),
|
||||
Version = iolist_to_binary(proplists:get_value(version, emqx_sys:info())),
|
||||
{200, #{token => Token,
|
||||
version => Version,
|
||||
license => #{edition => emqx_release:edition()}
|
||||
}};
|
||||
{200, #{
|
||||
token => Token,
|
||||
version => Version,
|
||||
license => #{edition => emqx_release:edition()}
|
||||
}};
|
||||
{error, R} ->
|
||||
?SLOG(info, #{msg => "Dashboard login failed", username => Username, reason => R}),
|
||||
{401, ?WRONG_USERNAME_OR_PWD, <<"Auth filed">>}
|
||||
end.
|
||||
|
||||
logout(_, #{body := #{<<"username">> := Username},
|
||||
headers := #{<<"authorization">> := <<"Bearer ", Token/binary>>}}) ->
|
||||
logout(_, #{
|
||||
body := #{<<"username">> := Username},
|
||||
headers := #{<<"authorization">> := <<"Bearer ", Token/binary>>}
|
||||
}) ->
|
||||
case emqx_dashboard_admin:destroy_token_by_username(Username, Token) of
|
||||
ok ->
|
||||
?SLOG(info, #{msg => "Dashboard logout successfully", username => Username}),
|
||||
|
@ -229,7 +244,6 @@ logout(_, #{body := #{<<"username">> := Username},
|
|||
|
||||
users(get, _Request) ->
|
||||
{200, emqx_dashboard_admin:all_users()};
|
||||
|
||||
users(post, #{body := Params}) ->
|
||||
Desc = maps:get(<<"description">>, Params, <<"">>),
|
||||
Username = maps:get(<<"username">>, Params),
|
||||
|
@ -243,8 +257,11 @@ users(post, #{body := Params}) ->
|
|||
?SLOG(info, #{msg => "Create dashboard success", username => Username}),
|
||||
{200, Result};
|
||||
{error, Reason} ->
|
||||
?SLOG(info, #{msg => "Create dashboard failed",
|
||||
username => Username, reason => Reason}),
|
||||
?SLOG(info, #{
|
||||
msg => "Create dashboard failed",
|
||||
username => Username,
|
||||
reason => Reason
|
||||
}),
|
||||
{400, ?BAD_REQUEST, Reason}
|
||||
end
|
||||
end.
|
||||
|
@ -257,7 +274,6 @@ user(put, #{bindings := #{username := Username}, body := Params}) ->
|
|||
{error, Reason} ->
|
||||
{404, ?USER_NOT_FOUND, Reason}
|
||||
end;
|
||||
|
||||
user(delete, #{bindings := #{username := Username}}) ->
|
||||
case Username == emqx_dashboard_admin:default_username() of
|
||||
true ->
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
|
||||
-behaviour(application).
|
||||
|
||||
-export([ start/2
|
||||
, stop/1
|
||||
]).
|
||||
-export([
|
||||
start/2,
|
||||
stop/1
|
||||
]).
|
||||
|
||||
-include("emqx_dashboard.hrl").
|
||||
|
||||
|
@ -33,7 +34,8 @@ start(_StartType, _StartArgs) ->
|
|||
{ok, _Result} = emqx_dashboard_admin:add_default_user(),
|
||||
ok = emqx_dashboard_config:add_handler(),
|
||||
{ok, Sup};
|
||||
{error, Reason} -> {error, Reason}
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
stop(_State) ->
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
|
||||
init(Req0, State) ->
|
||||
?SLOG(warning, #{msg => "unexpected_api_access", request => Req0}),
|
||||
Req = cowboy_req:reply(404,
|
||||
Req = cowboy_req:reply(
|
||||
404,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
<<"{\"code\": \"API_NOT_EXIST\", \"message\": \"Request Path Not Found\"}">>,
|
||||
Req0),
|
||||
Req0
|
||||
),
|
||||
{ok, Req, State}.
|
||||
|
|
|
@ -16,17 +16,17 @@
|
|||
|
||||
-module(emqx_dashboard_cli).
|
||||
|
||||
-export([ load/0
|
||||
, admins/1
|
||||
, unload/0
|
||||
]).
|
||||
-export([
|
||||
load/0,
|
||||
admins/1,
|
||||
unload/0
|
||||
]).
|
||||
|
||||
load() ->
|
||||
emqx_ctl:register_command(admins, {?MODULE, admins}, []).
|
||||
|
||||
admins(["add", Username, Password]) ->
|
||||
admins(["add", Username, Password, ""]);
|
||||
|
||||
admins(["add", Username, Password, Desc]) ->
|
||||
case emqx_dashboard_admin:add_user(bin(Username), bin(Password), bin(Desc)) of
|
||||
{ok, _} ->
|
||||
|
@ -34,20 +34,20 @@ admins(["add", Username, Password, Desc]) ->
|
|||
{error, Reason} ->
|
||||
emqx_ctl:print("Error: ~p~n", [Reason])
|
||||
end;
|
||||
|
||||
admins(["passwd", Username, Password]) ->
|
||||
Status = emqx_dashboard_admin:change_password(bin(Username), bin(Password)),
|
||||
Status = emqx_dashboard_admin:change_password(bin(Username), bin(Password)),
|
||||
emqx_ctl:print("~p~n", [Status]);
|
||||
|
||||
admins(["del", Username]) ->
|
||||
Status = emqx_dashboard_admin:remove_user(bin(Username)),
|
||||
Status = emqx_dashboard_admin:remove_user(bin(Username)),
|
||||
emqx_ctl:print("~p~n", [Status]);
|
||||
|
||||
admins(_) ->
|
||||
emqx_ctl:usage(
|
||||
[{"admins add <Username> <Password> <Description>", "Add dashboard user"},
|
||||
{"admins passwd <Username> <Password>", "Reset dashboard user password"},
|
||||
{"admins del <Username>", "Delete dashboard user" }]).
|
||||
[
|
||||
{"admins add <Username> <Password> <Description>", "Add dashboard user"},
|
||||
{"admins passwd <Username> <Password>", "Reset dashboard user password"},
|
||||
{"admins del <Username>", "Delete dashboard user"}
|
||||
]
|
||||
).
|
||||
|
||||
unload() ->
|
||||
emqx_ctl:unregister_command(admins).
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
|
||||
-include_lib("emqx/include/http_api.hrl").
|
||||
|
||||
-export([ all/0
|
||||
, list/0
|
||||
, look_up/1
|
||||
, description/1
|
||||
, format/1
|
||||
]).
|
||||
-export([
|
||||
all/0,
|
||||
list/0,
|
||||
look_up/1,
|
||||
description/1,
|
||||
format/1
|
||||
]).
|
||||
|
||||
all() ->
|
||||
[Name || {Name, _Description} <- ?ERROR_CODES].
|
||||
|
@ -51,6 +52,6 @@ description(Code, [_ | List]) ->
|
|||
|
||||
format({Code, Description}) ->
|
||||
#{
|
||||
code => Code,
|
||||
code => Code,
|
||||
description => Description
|
||||
}.
|
||||
|
|
|
@ -21,16 +21,18 @@
|
|||
-include("emqx_dashboard.hrl").
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
|
||||
-export([ api_spec/0
|
||||
, fields/1
|
||||
, paths/0
|
||||
, schema/1
|
||||
, namespace/0
|
||||
]).
|
||||
-export([
|
||||
api_spec/0,
|
||||
fields/1,
|
||||
paths/0,
|
||||
schema/1,
|
||||
namespace/0
|
||||
]).
|
||||
|
||||
-export([ error_codes/2
|
||||
, error_code/2
|
||||
]).
|
||||
-export([
|
||||
error_codes/2,
|
||||
error_code/2
|
||||
]).
|
||||
|
||||
namespace() -> "dashboard".
|
||||
|
||||
|
@ -38,8 +40,9 @@ api_spec() ->
|
|||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}).
|
||||
|
||||
paths() ->
|
||||
[ "/error_codes"
|
||||
, "/error_codes/:code"
|
||||
[
|
||||
"/error_codes",
|
||||
"/error_codes/:code"
|
||||
].
|
||||
|
||||
schema("/error_codes") ->
|
||||
|
@ -60,10 +63,12 @@ schema("/error_codes/:code") ->
|
|||
security => [],
|
||||
description => <<"API Error Codes">>,
|
||||
parameters => [
|
||||
{code, hoconsc:mk(hoconsc:enum(emqx_dashboard_error_code:all()), #{
|
||||
desc => <<"API Error Codes">>,
|
||||
in => path,
|
||||
example => hd(emqx_dashboard_error_code:all())})}
|
||||
{code,
|
||||
hoconsc:mk(hoconsc:enum(emqx_dashboard_error_code:all()), #{
|
||||
desc => <<"API Error Codes">>,
|
||||
in => path,
|
||||
example => hd(emqx_dashboard_error_code:all())
|
||||
})}
|
||||
],
|
||||
responses => #{
|
||||
200 => hoconsc:ref(?MODULE, error_code)
|
||||
|
@ -77,7 +82,6 @@ fields(error_code) ->
|
|||
{description, hoconsc:mk(string(), #{desc => <<"Description">>})}
|
||||
].
|
||||
|
||||
|
||||
error_codes(_, _) ->
|
||||
{200, emqx_dashboard_error_code:list()}.
|
||||
|
||||
|
|
|
@ -9,25 +9,28 @@
|
|||
|
||||
-behaviour(minirest_api).
|
||||
|
||||
-export([ api_spec/0]).
|
||||
-export([api_spec/0]).
|
||||
|
||||
-export([ paths/0
|
||||
, schema/1
|
||||
, fields/1
|
||||
]).
|
||||
-export([
|
||||
paths/0,
|
||||
schema/1,
|
||||
fields/1
|
||||
]).
|
||||
|
||||
-export([ monitor/2
|
||||
, monitor_current/2
|
||||
]).
|
||||
-export([
|
||||
monitor/2,
|
||||
monitor_current/2
|
||||
]).
|
||||
|
||||
api_spec() ->
|
||||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true, translate_body => true}).
|
||||
|
||||
paths() ->
|
||||
[ "/monitor"
|
||||
, "/monitor/nodes/:node"
|
||||
, "/monitor_current"
|
||||
, "/monitor_current/nodes/:node"
|
||||
[
|
||||
"/monitor",
|
||||
"/monitor/nodes/:node",
|
||||
"/monitor_current",
|
||||
"/monitor_current/nodes/:node"
|
||||
].
|
||||
|
||||
schema("/monitor") ->
|
||||
|
@ -43,7 +46,6 @@ schema("/monitor") ->
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
schema("/monitor/nodes/:node") ->
|
||||
#{
|
||||
'operationId' => monitor,
|
||||
|
@ -57,7 +59,6 @@ schema("/monitor/nodes/:node") ->
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
schema("/monitor_current") ->
|
||||
#{
|
||||
'operationId' => monitor_current,
|
||||
|
@ -69,7 +70,6 @@ schema("/monitor_current") ->
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
schema("/monitor_current/nodes/:node") ->
|
||||
#{
|
||||
'operationId' => monitor_current,
|
||||
|
@ -102,17 +102,19 @@ parameter_node() ->
|
|||
},
|
||||
{node, hoconsc:mk(binary(), Info)}.
|
||||
|
||||
|
||||
fields(sampler) ->
|
||||
Samplers =
|
||||
[{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|
||||
|| SamplerName <- ?SAMPLER_LIST],
|
||||
[
|
||||
{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|
||||
|| SamplerName <- ?SAMPLER_LIST
|
||||
],
|
||||
[{time_stamp, hoconsc:mk(non_neg_integer(), #{desc => <<"Timestamp">>})} | Samplers];
|
||||
|
||||
fields(sampler_current) ->
|
||||
Names = maps:values(?DELTA_SAMPLER_RATE_MAP) ++ ?GAUGE_SAMPLER_LIST,
|
||||
[{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|
||||
|| SamplerName <- Names].
|
||||
[
|
||||
{SamplerName, hoconsc:mk(integer(), #{desc => swagger_desc(SamplerName)})}
|
||||
|| SamplerName <- Names
|
||||
].
|
||||
|
||||
%% -------------------------------------------------------------------------------------------------
|
||||
%% API
|
||||
|
@ -141,26 +143,39 @@ monitor_current(get, #{bindings := Bindings}) ->
|
|||
%% -------------------------------------------------------------------------------------------------
|
||||
%% Internal
|
||||
|
||||
swagger_desc(received) -> swagger_desc_format("Received messages ");
|
||||
swagger_desc(received_bytes) -> swagger_desc_format("Received bytes ");
|
||||
swagger_desc(sent) -> swagger_desc_format("Sent messages ");
|
||||
swagger_desc(sent_bytes) -> swagger_desc_format("Sent bytes ");
|
||||
swagger_desc(dropped) -> swagger_desc_format("Dropped messages ");
|
||||
swagger_desc(received) ->
|
||||
swagger_desc_format("Received messages ");
|
||||
swagger_desc(received_bytes) ->
|
||||
swagger_desc_format("Received bytes ");
|
||||
swagger_desc(sent) ->
|
||||
swagger_desc_format("Sent messages ");
|
||||
swagger_desc(sent_bytes) ->
|
||||
swagger_desc_format("Sent bytes ");
|
||||
swagger_desc(dropped) ->
|
||||
swagger_desc_format("Dropped messages ");
|
||||
swagger_desc(subscriptions) ->
|
||||
<<"Subscriptions at the time of sampling."
|
||||
" Can only represent the approximate state">>;
|
||||
<<
|
||||
"Subscriptions at the time of sampling."
|
||||
" Can only represent the approximate state"
|
||||
>>;
|
||||
swagger_desc(topics) ->
|
||||
<<"Count topics at the time of sampling."
|
||||
" Can only represent the approximate state">>;
|
||||
<<
|
||||
"Count topics at the time of sampling."
|
||||
" Can only represent the approximate state"
|
||||
>>;
|
||||
swagger_desc(connections) ->
|
||||
<<"Connections at the time of sampling."
|
||||
" Can only represent the approximate state">>;
|
||||
|
||||
swagger_desc(received_msg_rate) -> swagger_desc_format("Dropped messages ", per);
|
||||
<<
|
||||
"Connections at the time of sampling."
|
||||
" Can only represent the approximate state"
|
||||
>>;
|
||||
swagger_desc(received_msg_rate) ->
|
||||
swagger_desc_format("Dropped messages ", per);
|
||||
%swagger_desc(received_bytes_rate) -> swagger_desc_format("Received bytes ", per);
|
||||
swagger_desc(sent_msg_rate) -> swagger_desc_format("Sent messages ", per);
|
||||
swagger_desc(sent_msg_rate) ->
|
||||
swagger_desc_format("Sent messages ", per);
|
||||
%swagger_desc(sent_bytes_rate) -> swagger_desc_format("Sent bytes ", per);
|
||||
swagger_desc(dropped_msg_rate) -> swagger_desc_format("Dropped messages ", per).
|
||||
swagger_desc(dropped_msg_rate) ->
|
||||
swagger_desc_format("Dropped messages ", per).
|
||||
|
||||
swagger_desc_format(Format) ->
|
||||
swagger_desc_format(Format, last).
|
||||
|
|
|
@ -28,8 +28,8 @@ start_link() ->
|
|||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
{ok, {{one_for_one, 10, 100},
|
||||
[
|
||||
{ok,
|
||||
{{one_for_one, 10, 100}, [
|
||||
?CHILD(emqx_dashboard_token),
|
||||
?CHILD(emqx_dashboard_monitor),
|
||||
?CHILD(emqx_dashboard_config)
|
||||
|
|
|
@ -407,7 +407,8 @@ trans_description(Spec, Hocon) ->
|
|||
Struct -> to_bin(Struct)
|
||||
end,
|
||||
case Desc of
|
||||
undefined -> Spec;
|
||||
undefined ->
|
||||
Spec;
|
||||
Desc ->
|
||||
Desc1 = binary:replace(Desc, [<<"</br>\n">>, <<"\n">>], <<"</br>">>, [global]),
|
||||
Spec#{description => Desc1}
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
|
||||
-include("emqx_dashboard.hrl").
|
||||
|
||||
-export([ sign/2
|
||||
, verify/1
|
||||
, lookup/1
|
||||
, destroy/1
|
||||
, destroy_by_username/1
|
||||
]).
|
||||
-export([
|
||||
sign/2,
|
||||
verify/1,
|
||||
lookup/1,
|
||||
destroy/1,
|
||||
destroy_by_username/1
|
||||
]).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
|
||||
|
@ -42,54 +43,60 @@
|
|||
|
||||
-export([start_link/0, salt/0]).
|
||||
|
||||
-export([ init/1
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
, terminate/2
|
||||
, code_change/3
|
||||
]).
|
||||
-export([
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% jwt function
|
||||
-spec(sign(Username :: binary(), Password :: binary()) ->
|
||||
{ok, Token :: binary()} | {error, Reason :: term()}).
|
||||
-spec sign(Username :: binary(), Password :: binary()) ->
|
||||
{ok, Token :: binary()} | {error, Reason :: term()}.
|
||||
sign(Username, Password) ->
|
||||
do_sign(Username, Password).
|
||||
|
||||
-spec(verify(Token :: binary()) -> Result :: ok | {error, token_timeout | not_found}).
|
||||
-spec verify(Token :: binary()) -> Result :: ok | {error, token_timeout | not_found}.
|
||||
verify(Token) ->
|
||||
do_verify(Token).
|
||||
|
||||
-spec(destroy(KeyOrKeys :: list() | binary() | #?ADMIN_JWT{}) -> ok).
|
||||
-spec destroy(KeyOrKeys :: list() | binary() | #?ADMIN_JWT{}) -> ok.
|
||||
destroy([]) ->
|
||||
ok;
|
||||
destroy(JWTorTokenList) when is_list(JWTorTokenList)->
|
||||
destroy(JWTorTokenList) when is_list(JWTorTokenList) ->
|
||||
lists:foreach(fun destroy/1, JWTorTokenList);
|
||||
destroy(#?ADMIN_JWT{token = Token}) ->
|
||||
destroy(Token);
|
||||
destroy(Token) when is_binary(Token)->
|
||||
destroy(Token) when is_binary(Token) ->
|
||||
do_destroy(Token).
|
||||
|
||||
-spec(destroy_by_username(Username :: binary()) -> ok).
|
||||
-spec destroy_by_username(Username :: binary()) -> ok.
|
||||
destroy_by_username(Username) ->
|
||||
do_destroy_by_username(Username).
|
||||
|
||||
%% @doc create 4 bytes salt.
|
||||
-spec(salt() -> binary()).
|
||||
-spec salt() -> binary().
|
||||
salt() ->
|
||||
<<X:16/big-unsigned-integer>> = crypto:strong_rand_bytes(2),
|
||||
iolist_to_binary(io_lib:format("~4.16.0b", [X])).
|
||||
|
||||
mnesia(boot) ->
|
||||
ok = mria:create_table(?TAB, [
|
||||
{type, set},
|
||||
{rlog_shard, ?DASHBOARD_SHARD},
|
||||
{storage, disc_copies},
|
||||
{record_name, ?ADMIN_JWT},
|
||||
{attributes, record_info(fields, ?ADMIN_JWT)},
|
||||
{storage_properties, [{ets, [{read_concurrency, true},
|
||||
{write_concurrency, true}]}]}]).
|
||||
{type, set},
|
||||
{rlog_shard, ?DASHBOARD_SHARD},
|
||||
{storage, disc_copies},
|
||||
{record_name, ?ADMIN_JWT},
|
||||
{attributes, record_info(fields, ?ADMIN_JWT)},
|
||||
{storage_properties, [
|
||||
{ets, [
|
||||
{read_concurrency, true},
|
||||
{write_concurrency, true}
|
||||
]}
|
||||
]}
|
||||
]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% jwt apply
|
||||
|
@ -110,15 +117,17 @@ do_sign(Username, Password) ->
|
|||
_ = mria:transaction(?DASHBOARD_SHARD, fun mnesia:write/1, [JWTRec]),
|
||||
{ok, Token}.
|
||||
|
||||
do_verify(Token)->
|
||||
do_verify(Token) ->
|
||||
case lookup(Token) of
|
||||
{ok, JWT = #?ADMIN_JWT{exptime = ExpTime}} ->
|
||||
case ExpTime > erlang:system_time(millisecond) of
|
||||
true ->
|
||||
NewJWT = JWT#?ADMIN_JWT{exptime = jwt_expiration_time()},
|
||||
{atomic, Res} = mria:transaction(?DASHBOARD_SHARD,
|
||||
fun mnesia:write/1,
|
||||
[NewJWT]),
|
||||
{atomic, Res} = mria:transaction(
|
||||
?DASHBOARD_SHARD,
|
||||
fun mnesia:write/1,
|
||||
[NewJWT]
|
||||
),
|
||||
Res;
|
||||
_ ->
|
||||
{error, token_timeout}
|
||||
|
@ -137,7 +146,7 @@ do_destroy_by_username(Username) ->
|
|||
|
||||
%%--------------------------------------------------------------------
|
||||
%% jwt internal util function
|
||||
-spec(lookup(Token :: binary()) -> {ok, #?ADMIN_JWT{}} | {error, not_found}).
|
||||
-spec lookup(Token :: binary()) -> {ok, #?ADMIN_JWT{}} | {error, not_found}.
|
||||
lookup(Token) ->
|
||||
Fun = fun() -> mnesia:read(?TAB, Token) end,
|
||||
case mria:ro_transaction(?DASHBOARD_SHARD, Fun) of
|
||||
|
@ -167,9 +176,9 @@ token_ttl() ->
|
|||
|
||||
format(Token, Username, ExpTime) ->
|
||||
#?ADMIN_JWT{
|
||||
token = Token,
|
||||
token = Token,
|
||||
username = Username,
|
||||
exptime = ExpTime
|
||||
exptime = ExpTime
|
||||
}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -210,8 +219,9 @@ timer_clean(Pid) ->
|
|||
|
||||
-dialyzer({nowarn_function, clean_expired_jwt/1}).
|
||||
clean_expired_jwt(Now) ->
|
||||
Spec = [{#?ADMIN_JWT{exptime = '$1', token = '$2', _ = '_'},
|
||||
[{'<', '$1', Now}], ['$2']}],
|
||||
{atomic, JWTList} = mria:ro_transaction(?DASHBOARD_SHARD,
|
||||
fun() -> mnesia:select(?TAB, Spec) end),
|
||||
Spec = [{#?ADMIN_JWT{exptime = '$1', token = '$2', _ = '_'}, [{'<', '$1', Now}], ['$2']}],
|
||||
{atomic, JWTList} = mria:ro_transaction(
|
||||
?DASHBOARD_SHARD,
|
||||
fun() -> mnesia:select(?TAB, Spec) end
|
||||
),
|
||||
ok = destroy(JWTList).
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
|
||||
-behaviour(emqx_bpapi).
|
||||
|
||||
-export([ introduced_in/0
|
||||
, do_sample/2
|
||||
, current_rate/1
|
||||
]).
|
||||
-export([
|
||||
introduced_in/0,
|
||||
do_sample/2,
|
||||
current_rate/1
|
||||
]).
|
||||
|
||||
-include("emqx_dashboard.hrl").
|
||||
-include_lib("emqx/include/bpapi.hrl").
|
||||
|
@ -29,7 +30,7 @@
|
|||
introduced_in() ->
|
||||
"5.0.0".
|
||||
|
||||
-spec do_sample(node(), Latest:: pos_integer() | infinity) -> list(map()) | emqx_rpc:badrpc().
|
||||
-spec do_sample(node(), Latest :: pos_integer() | infinity) -> list(map()) | emqx_rpc:badrpc().
|
||||
do_sample(Node, Latest) ->
|
||||
rpc:call(Node, emqx_dashboard_monitor, do_sample, [Node, Latest], ?RPC_TIMEOUT).
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ end_per_suite(Config) ->
|
|||
|
||||
end_per_testcase(_, _Config) ->
|
||||
All = emqx_dashboard_admin:all_users(),
|
||||
[emqx_dashboard_admin:remove_user(Name) || #{username := Name} <- All].
|
||||
[emqx_dashboard_admin:remove_user(Name) || #{username := Name} <- All].
|
||||
|
||||
end_suite() ->
|
||||
application:unload(emqx_management),
|
||||
|
@ -144,7 +144,7 @@ t_update_user(_) ->
|
|||
UpdateUser = maps:get(username, NewUserInfo),
|
||||
NewDesc = maps:get(description, NewUserInfo),
|
||||
|
||||
{error,<<"username_not_found">>} = emqx_dashboard_admin:update_user(BadUpdateUser, NewDesc),
|
||||
{error, <<"username_not_found">>} = emqx_dashboard_admin:update_user(BadUpdateUser, NewDesc),
|
||||
ok.
|
||||
|
||||
t_change_password(_) ->
|
||||
|
@ -160,7 +160,7 @@ t_change_password(_) ->
|
|||
|
||||
{ok, ok} = emqx_dashboard_admin:change_password(User, OldPassword, NewPassword),
|
||||
%% change pwd again
|
||||
{error,<<"password_error">>} =
|
||||
{error, <<"password_error">>} =
|
||||
emqx_dashboard_admin:change_password(User, OldPassword, NewPassword),
|
||||
|
||||
{error, <<"username_not_found">>} =
|
||||
|
@ -185,4 +185,3 @@ t_clean_token(_) ->
|
|||
timer:sleep(5),
|
||||
{error, not_found} = emqx_dashboard_admin:verify_token(Token2),
|
||||
ok.
|
||||
|
||||
|
|
|
@ -158,7 +158,8 @@ banned(post, #{body := Body}) ->
|
|||
{400, 'BAD_REQUEST', list_to_binary(Reason)};
|
||||
Ban ->
|
||||
case emqx_banned:create(Ban) of
|
||||
{ok, Banned} -> {200, format(Banned)};
|
||||
{ok, Banned} ->
|
||||
{200, format(Banned)};
|
||||
{error, {already_exist, Old}} ->
|
||||
OldBannedFormat = emqx_json:encode(format(Old)),
|
||||
{400, 'ALREADY_EXISTS', OldBannedFormat}
|
||||
|
|
|
@ -33,3 +33,5 @@ b168102615e574df15ec6a91304747b4637a9171
|
|||
b4451823350ec46126c49ca915b4b169dd4cf49e
|
||||
# reformat apps/emqx_auto_subscribe and apps/emqx_conf
|
||||
a4feb3e6e95c18cb531416112e57520c5ba00d40
|
||||
# reformat apps/emqx_dashboard
|
||||
fda246814b38ced2d229c42307c447970b09f3ab
|
||||
|
|
|
@ -17,6 +17,7 @@ APPS+=( 'apps/emqx_management')
|
|||
APPS+=( 'apps/emqx_psk')
|
||||
APPS+=( 'apps/emqx_plugin_libs' 'apps/emqx_machine' 'apps/emqx_statsd' )
|
||||
APPS+=( 'apps/emqx_auto_subscribe' 'apps/emqx_conf')
|
||||
APPS+=( 'apps/emqx_dashboard')
|
||||
|
||||
for app in "${APPS[@]}"; do
|
||||
echo "$app ..."
|
||||
|
|
Loading…
Reference in New Issue