chore: dashboard format code

This commit is contained in:
DDDHuang 2022-04-26 16:54:14 +08:00
parent 2ea66ebcee
commit 07444e3da5
19 changed files with 363 additions and 276 deletions

View File

@ -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
}).

View File

@ -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]}.

View File

@ -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"}
]}
]}.

View File

@ -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.

View File

@ -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 ->

View File

@ -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) ->

View File

@ -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}.

View File

@ -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).

View File

@ -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
}.

View File

@ -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()}.

View File

@ -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).

View File

@ -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)

View File

@ -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}

View File

@ -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).

View File

@ -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).

View File

@ -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.

View File

@ -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}

View File

@ -33,3 +33,5 @@ b168102615e574df15ec6a91304747b4637a9171
b4451823350ec46126c49ca915b4b169dd4cf49e
# reformat apps/emqx_auto_subscribe and apps/emqx_conf
a4feb3e6e95c18cb531416112e57520c5ba00d40
# reformat apps/emqx_dashboard
fda246814b38ced2d229c42307c447970b09f3ab

View File

@ -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 ..."