feat(dashboard): Update the configuration file to hocon

This commit is contained in:
Turtle 2021-07-26 13:37:28 +08:00 committed by turtleDeng
parent 3b1303340a
commit 569d54a4c0
12 changed files with 189 additions and 401 deletions

View File

@ -91,6 +91,7 @@ includes() ->
, "emqx_bridge_mqtt" , "emqx_bridge_mqtt"
, "emqx_modules" , "emqx_modules"
, "emqx_management" , "emqx_management"
, "emqx_dashboard"
, "emqx_gateway" , "emqx_gateway"
]. ].
-endif. -endif.

View File

@ -1,130 +1,41 @@
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## EMQ X Dashboard ## Dashboard for EMQ X
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Default user's login name. emqx_dashboard:{
## default_username: "admin"
## Value: String default_password: "public"
dashboard.default_user.login = admin listeners: [
{
## Default user's password. num_acceptors: 4
## max_connections: 512
## Value: String protocol: http
dashboard.default_user.password = public port: 18083
backlog: 512
##-------------------------------------------------------------------- send_timeout: 15s
## HTTP Listener send_timeout_close: true
inet6: false
## The port that the Dashboard HTTP listener will bind. ipv6_v6only: false
## }
## Value: Port ## ,
## ## {
## Examples: 18083 ## protocol: https
dashboard.listener.http.port = 18083 ## port: 8081
## acceptors: 2
## The acceptor pool for external Dashboard HTTP listener. ## backlog: 512
## ## send_timeout: 15s
## Value: Number ## send_timeout_close: true
dashboard.listener.http.acceptors = 4 ## inet6: false
## ipv6_v6only: false
## Maximum number of concurrent Dashboard HTTP connections. ## certfile = "etc/certs/cert.pem"
## ## keyfile = "etc/certs/key.pem"
## Value: Number ## cacertfile = "etc/certs/cacert.pem"
dashboard.listener.http.max_clients = 512 ## verify = verify_peer
## tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## Set up the socket for IPv6. ## ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## ## fail_if_no_peer_cert = true
## Value: false | true ## inet6 = false
dashboard.listener.http.inet6 = false ## ipv6_v6only = false
## }
## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6. ]
## }
## Value: false | true
dashboard.listener.http.ipv6_v6only = false
##--------------------------------------------------------------------
## HTTPS Listener
## The port that the Dashboard HTTPS listener will bind.
##
## Value: Port
##
## Examples: 18084
## dashboard.listener.https.port = 18084
## The acceptor pool for external Dashboard HTTPS listener.
##
## Value: Number
## dashboard.listener.https.acceptors = 2
## Maximum number of concurrent Dashboard HTTPS connections.
##
## Value: Number
## dashboard.listener.https.max_clients = 512
## Set up the socket for IPv6.
##
## Value: false | true
## dashboard.listener.https.inet6 = false
## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6.
##
## Value: false | true
## dashboard.listener.https.ipv6_v6only = false
## Path to the file containing the user's private PEM-encoded key.
##
## Value: File
## dashboard.listener.https.keyfile = "etc/certs/key.pem"
## Path to a file containing the user certificate.
##
## Value: File
## dashboard.listener.https.certfile = "etc/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates.
##
## Value: File
## dashboard.listener.https.cacertfile = "etc/certs/cacert.pem"
## See: 'listener.ssl.<name>.dhfile' in emq.conf
##
## Value: File
## dashboard.listener.https.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
## See: 'listener.ssl.<name>.verify' in emq.conf
##
## Value: verify_peer | verify_none
## dashboard.listener.https.verify = verify_peer
## See: 'listener.ssl.<name>.fail_if_no_peer_cert' in emq.conf
##
## Value: false | true
## dashboard.listener.https.fail_if_no_peer_cert = true
## TLS versions only to protect from POODLE attack.
##
## Value: String, seperated by ','
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## dashboard.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## See: 'listener.ssl.<name>.ciphers' in emq.conf
##
## Value: Ciphers
## dashboard.listener.https.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
##
## Value: on | off
## dashboard.listener.https.secure_renegotiate = off
## See: 'listener.ssl.<name>.reuse_sessions' in emq.conf
##
## Value: on | off
## dashboard.listener.https.reuse_sessions = on
## See: 'listener.ssl.<name>.honor_cipher_order' in emq.conf
##
## Value: on | off
## dashboard.listener.https.honor_cipher_order = on

View File

@ -14,7 +14,7 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-record(mqtt_admin, {username, password, tags}). -record(mqtt_admin, {username, password, tags, role = undefined}).
-type(mqtt_admin() :: #mqtt_admin{}). -type(mqtt_admin() :: #mqtt_admin{}).

View File

@ -1,152 +0,0 @@
%%-*- mode: erlang -*-
%% emqx_dashboard config mapping
{mapping, "dashboard.default_user.login", "emqx_dashboard.default_user_username", [
{datatype, string}
]}.
{mapping, "dashboard.default_user.password", "emqx_dashboard.default_user_passwd", [
{datatype, string},
{override_env, "EMQX_ADMIN_PASSWORD"}
]}.
{mapping, "dashboard.listener.http.port", "emqx_dashboard.listeners", [
{datatype, integer}
]}.
{mapping, "dashboard.listener.http.acceptors", "emqx_dashboard.listeners", [
{default, 4},
{datatype, integer}
]}.
{mapping, "dashboard.listener.http.max_clients", "emqx_dashboard.listeners", [
{default, 512},
{datatype, integer}
]}.
{mapping, "dashboard.listener.http.access.$id", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.http.inet6", "emqx_dashboard.listeners", [
{default, false},
{datatype, {enum, [true, false]}}
]}.
{mapping, "dashboard.listener.http.ipv6_v6only", "emqx_dashboard.listeners", [
{default, false},
{datatype, {enum, [true, false]}}
]}.
{mapping, "dashboard.listener.https.port", "emqx_dashboard.listeners", [
{datatype, integer}
]}.
{mapping, "dashboard.listener.https.acceptors", "emqx_dashboard.listeners", [
{default, 8},
{datatype, integer}
]}.
{mapping, "dashboard.listener.https.max_clients", "emqx_dashboard.listeners", [
{default, 64},
{datatype, integer}
]}.
{mapping, "dashboard.listener.https.inet6", "emqx_dashboard.listeners", [
{default, false},
{datatype, {enum, [true, false]}}
]}.
{mapping, "dashboard.listener.https.ipv6_v6only", "emqx_dashboard.listeners", [
{default, false},
{datatype, {enum, [true, false]}}
]}.
{mapping, "dashboard.listener.https.tls_versions", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.dhfile", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.keyfile", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.certfile", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.cacertfile", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.verify", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.fail_if_no_peer_cert", "emqx_dashboard.listeners", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "dashboard.listener.https.ciphers", "emqx_dashboard.listeners", [
{datatype, string}
]}.
{mapping, "dashboard.listener.https.secure_renegotiate", "emqx_dashboard.listeners", [
{datatype, flag}
]}.
{mapping, "dashboard.listener.https.reuse_sessions", "emqx_dashboard.listeners", [
{default, on},
{datatype, flag}
]}.
{mapping, "dashboard.listener.https.honor_cipher_order", "emqx_dashboard.listeners", [
{datatype, flag}
]}.
{translation, "emqx_dashboard.listeners", fun(Conf) ->
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
LisOpts = fun(Prefix) ->
Filter([{num_acceptors, cuttlefish:conf_get(Prefix ++ ".acceptors", Conf)},
{max_connections, cuttlefish:conf_get(Prefix ++ ".max_clients", Conf)},
{inet6, cuttlefish:conf_get(Prefix ++ ".inet6", Conf)},
{ipv6_v6only, cuttlefish:conf_get(Prefix ++ ".ipv6_v6only", Conf)}])
end,
SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end,
SslOpts = fun(Prefix) ->
Versions = case SplitFun(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf, undefined)) of
undefined -> undefined;
L -> [list_to_atom(V) || V <- L]
end,
Filter([{versions, Versions},
{ciphers, SplitFun(cuttlefish:conf_get(Prefix ++ ".ciphers", Conf, undefined))},
{dhfile, cuttlefish:conf_get(Prefix ++ ".dhfile", Conf, undefined)},
{keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)},
{certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)},
{cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)},
{verify, cuttlefish:conf_get(Prefix ++ ".verify", Conf, undefined)},
{fail_if_no_peer_cert, cuttlefish:conf_get(Prefix ++ ".fail_if_no_peer_cert", Conf, undefined)},
{secure_renegotiate, cuttlefish:conf_get(Prefix ++ ".secure_renegotiate", Conf, undefined)},
{reuse_sessions, cuttlefish:conf_get(Prefix ++ ".reuse_sessions", Conf, undefined)},
{honor_cipher_order, cuttlefish:conf_get(Prefix ++ ".honor_cipher_order", Conf, undefined)}])
end,
lists:append(
lists:map(
fun(Proto) ->
Prefix = "dashboard.listener." ++ atom_to_list(Proto),
case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
undefined -> [];
Port ->
[{Proto, Port, case Proto of
http -> LisOpts(Prefix);
https -> LisOpts(Prefix) ++ SslOpts(Prefix)
end}]
end
end, [http, https]))
end}.

View File

@ -1,6 +1,6 @@
{application, emqx_dashboard, {application, emqx_dashboard,
[{description, "EMQ X Web Dashboard"}, [{description, "EMQ X Web Dashboard"},
{vsn, "4.4.0"}, % strict semver, bump manually! {vsn, "5.0.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_dashboard_sup]}, {registered, [emqx_dashboard_sup]},
{applications, [kernel,stdlib,mnesia,minirest]}, {applications, [kernel,stdlib,mnesia,minirest]},

View File

@ -1,16 +0,0 @@
%% -*- mode: erlang -*-
{VSN,
[ {"4.3.0",
%% load all plugins
%% NOTE: this depends on the fact that emqx_dashboard is always
%% the last application gets upgraded
[ {apply, {emqx_plugins, load, []}}
]},
{<<".*">>, []}
],
[ {"4.3.0",
[ {apply, {emqx_plugins, load, []}}
]},
{<<".*">>, []}
]
}.

View File

@ -16,114 +16,112 @@
-module(emqx_dashboard). -module(emqx_dashboard).
-include_lib("emqx/include/emqx.hrl"). -define(APP, ?MODULE).
-include_lib("emqx/include/logger.hrl").
%%-import(proplists, [get_value/3]).
-export([ start_listeners/0 -export([ start_listeners/0
, stop_listeners/0 , stop_listeners/0
, start_listener/1 , start_listener/1
, stop_listener/1 , stop_listener/1]).
]).
%% for minirest %% Authorization
-export([ filter/1 -export([authorize_appid/1]).
, is_authorized/1
]).
-define(APP, ?MODULE).
-define(BASE_PATH, "/api/v5").
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Start/Stop listeners. %% Start/Stop Listeners
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
start_listeners() -> start_listeners() ->
lists:foreach(fun(Listener) -> start_listener(Listener) end, listeners()). lists:foreach(fun start_listener/1, listeners()).
%% Start HTTP Listener
start_listener(_) -> ok.
%% TODO: V5 API
%%start_listener({Proto, Port, Options}) when Proto == http ->
%% Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
%% {"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
%% {"/api/v4/[...]", minirest, http_handlers()}],
%% minirest:start_http(listener_name(Proto), ranch_opts(Port, Options), Dispatch);
%%
%%start_listener({Proto, Port, Options}) when Proto == https ->
%% Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
%% {"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
%% {"/api/v4/[...]", minirest, http_handlers()}],
%% minirest:start_https(listener_name(Proto), ranch_opts(Port, Options), Dispatch).
%%
%%ranch_opts(Port, Options0) ->
%% NumAcceptors = get_value(num_acceptors, Options0, 4),
%% MaxConnections = get_value(max_connections, Options0, 512),
%% Options = lists:foldl(fun({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors ->
%% Acc;
%% ({inet6, true}, Acc) -> [inet6 | Acc];
%% ({inet6, false}, Acc) -> Acc;
%% ({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
%% ({ipv6_v6only, false}, Acc) -> Acc;
%% ({K, V}, Acc)->
%% [{K, V} | Acc]
%% end, [], Options0),
%% #{num_acceptors => NumAcceptors,
%% max_connections => MaxConnections,
%% socket_opts => [{port, Port} | Options]}.
stop_listeners() -> stop_listeners() ->
lists:foreach(fun(Listener) -> stop_listener(Listener) end, listeners()). lists:foreach(fun stop_listener/1, listeners()).
stop_listener(_) -> start_listener({Proto, Port, Options}) ->
ok. {ok, _} = application:ensure_all_started(minirest),
%% TODO: V5 API Authorization = {?MODULE, authorize_appid},
%%stop_listener({Proto, _Port, _}) -> RanchOptions = ranch_opts(Port, Options),
%% minirest:stop_http(listener_name(Proto)). GlobalSpec = #{
openapi => "3.0.0",
info => #{title => "EMQ X Dashboard API", version => "5.0.0"},
servers => [#{url => ?BASE_PATH}],
components => #{
schemas => #{},
securitySchemes => #{
application => #{
type => apiKey,
name => "authorization",
in => header}}}},
Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
{"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}}],
Minirest = #{
protocol => Proto,
base_path => ?BASE_PATH,
apps => apps(),
authorization => Authorization,
security => [#{application => []}],
swagger_global_spec => GlobalSpec,
dispatch => Dispatch},
MinirestOptions = maps:merge(Minirest, RanchOptions),
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
io:format("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
apps() ->
[App || {App, _, _} <- application:loaded_applications(),
case re:run(atom_to_list(App), "^emqx") of
{match,[{0,4}]} -> true;
_ -> false
end].
ranch_opts(Port, Options0) ->
Options = lists:foldl(
fun
({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors -> Acc;
({inet6, true}, Acc) -> [inet6 | Acc];
({inet6, false}, Acc) -> Acc;
({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
({ipv6_v6only, false}, Acc) -> Acc;
({K, V}, Acc)->
[{K, V} | Acc]
end, [], Options0),
maps:from_list([{port, Port} | Options]).
stop_listener({Proto, Port, _}) ->
io:format("Stop dashboard listener on ~s successfully.~n",[format(Port)]),
minirest:stop(listener_name(Proto)).
listeners() -> listeners() ->
application:get_env(?APP, listeners, []). [{Protocol, Port, maps:to_list(maps:without([protocol, port], Map))}
|| Map = #{protocol := Protocol,port := Port}
<- emqx_config:get([emqx_dashboard, listeners], [])].
%%listener_name(Proto) -> listener_name(Proto) ->
%% list_to_atom(atom_to_list(Proto) ++ ":dashboard"). list_to_atom(atom_to_list(Proto) ++ ":dashboard").
%%-------------------------------------------------------------------- authorize_appid(Req) ->
%% HTTP Handlers and Dispatcher
%%--------------------------------------------------------------------
%%http_handlers() ->
%% Plugins = lists:map(fun(Plugin) -> Plugin#plugin.name end, emqx_plugins:list()),
%% [{"/api/v4/",
%% minirest:handler(#{apps => Plugins ++ [emqx_modules],
%% filter => fun ?MODULE:filter/1}),
%% [{authorization, fun ?MODULE:is_authorized/1}]}].
%%--------------------------------------------------------------------
%% Basic Authorization
%%--------------------------------------------------------------------
is_authorized(Req) ->
is_authorized(binary_to_list(cowboy_req:path(Req)), Req).
is_authorized("/api/v4/auth", _Req) ->
true;
is_authorized(_Path, Req) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of case cowboy_req:parse_header(<<"authorization">>, Req) of
{basic, Username, Password} -> {basic, Username, Password} ->
case emqx_dashboard_admin:check(iolist_to_binary(Username), case emqx_dashboard_admin:check(iolist_to_binary(Username),
iolist_to_binary(Password)) of iolist_to_binary(Password)) of
ok -> true; ok ->
{error, Reason} -> ok;
?LOG(error, "[Dashboard] Authorization Failure: username=~s, reason=~p", {error, _} ->
[Username, Reason]), {401, #{<<"WWW-Authenticate">> =>
false <<"Basic Realm=\"minirest-server\"">>},
<<"UNAUTHORIZED">>}
end; end;
_ -> false _ ->
{401, #{<<"WWW-Authenticate">> =>
<<"Basic Realm=\"minirest-server\"">>},
<<"UNAUTHORIZED">>}
end. end.
filter(#{app := emqx_modules}) -> true; format(Port) when is_integer(Port) ->
filter(#{app := App}) -> io_lib:format("0.0.0.0:~w", [Port]);
case emqx_plugins:find_plugin(App) of format({Addr, Port}) when is_list(Addr) ->
false -> false; io_lib:format("~s:~w", [Addr, Port]);
Plugin -> Plugin#plugin.active format({Addr, Port}) when is_tuple(Addr) ->
end. io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).

View File

@ -182,7 +182,7 @@ check(Username, Password) ->
init([]) -> init([]) ->
%% Add default admin user %% Add default admin user
_ = add_default_user(binenv(default_user_username), binenv(default_user_passwd)), _ = add_default_user(binenv(default_username), binenv(default_password)),
{ok, state}. {ok, state}.
handle_call(_Req, _From, State) -> handle_call(_Req, _From, State) ->
@ -217,7 +217,7 @@ salt() ->
<<Salt:32>>. <<Salt:32>>.
binenv(Key) -> binenv(Key) ->
iolist_to_binary(application:get_env(emqx_dashboard, Key, "")). iolist_to_binary(emqx_config:get([emqx_dashboard, Key], "")).
add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) -> add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) ->
igonre; igonre;

View File

@ -0,0 +1,55 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_dashboard_schema).
-include_lib("typerefl/include/types.hrl").
-export([ structs/0
, fields/1]).
structs() -> ["emqx_dashboard"].
fields("emqx_dashboard") ->
[ {listeners, hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"),
hoconsc:ref(?MODULE, "https")]))}
, {default_username, fun default_username/1}
, {default_password, fun default_password/1}
];
fields("http") ->
[ {"protocol", hoconsc:enum([http, https])}
, {"port", emqx_schema:t(integer(), undefined, 8081)}
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
, {"send_timeout", emqx_schema:t(emqx_schema:duration(), undefined, "15s")}
, {"send_timeout_close", emqx_schema:t(boolean(), undefined, true)}
, {"inet6", emqx_schema:t(boolean(), undefined, false)}
, {"ipv6_v6only", emqx_schema:t(boolean(), undefined, false)}
];
fields("https") ->
emqx_schema:ssl(#{enable => true}) ++ fields("http").
default_username(type) -> string();
default_username(default) -> "admin";
default_username(nullable) -> false;
default_username(_) -> undefined.
default_password(type) -> string();
default_password(default) -> "public";
default_password(nullable) -> false;
default_password(_) -> undefined.

View File

@ -63,7 +63,7 @@ start_listener({Proto, Port, Options}) ->
swagger_global_spec => GlobalSpec}, swagger_global_spec => GlobalSpec},
MinirestOptions = maps:merge(Minirest, RanchOptions), MinirestOptions = maps:merge(Minirest, RanchOptions),
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions), {ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
io:format("Start ~p listener on ~p successfully.", [listener_name(Proto), Port]). io:format("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
apps() -> apps() ->
Apps = [App || {App, _, _} <- application:loaded_applications(), Apps = [App || {App, _, _} <- application:loaded_applications(),

View File

@ -269,6 +269,7 @@ relx_apps(ReleaseType) ->
, emqx_bridge_mqtt , emqx_bridge_mqtt
, emqx_modules , emqx_modules
, emqx_management , emqx_management
, emqx_dashboard
, emqx_retainer , emqx_retainer
, emqx_statsd , emqx_statsd
] ]

View File

@ -15,22 +15,12 @@ main(_) ->
{ok, Bin} = file:read_file(BaseConf), {ok, Bin} = file:read_file(BaseConf),
Apps = filelib:wildcard("emqx_*", "apps/"), Apps = filelib:wildcard("emqx_*", "apps/"),
Conf = lists:foldl(fun(App, Acc) -> Conf = lists:foldl(fun(App, Acc) ->
case lists:member(App, ["emqx_exhook", Filename = filename:join([apps, App, "etc", App]) ++ ".conf",
"emqx_exproto", case filelib:is_regular(Filename) of
"emqx_lwm2m", true ->
"emqx_sn", {ok, Bin1} = file:read_file(Filename),
"emqx_coap", [Acc, io_lib:nl(), Bin1];
"emqx_stomp", false -> Acc
"emqx_dashboard"]) of
true -> Acc;
false ->
Filename = filename:join([apps, App, "etc", App]) ++ ".conf",
case filelib:is_regular(Filename) of
true ->
{ok, Bin1} = file:read_file(Filename),
[Acc, io_lib:nl(), Bin1];
false -> Acc
end
end end
end, Bin, Apps), end, Bin, Apps),
ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf). ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf).