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_modules"
, "emqx_management"
, "emqx_dashboard"
, "emqx_gateway"
].
-endif.

View File

@ -1,130 +1,41 @@
##--------------------------------------------------------------------
## EMQ X Dashboard
## Dashboard for EMQ X
##--------------------------------------------------------------------
## Default user's login name.
##
## Value: String
dashboard.default_user.login = admin
## Default user's password.
##
## Value: String
dashboard.default_user.password = public
##--------------------------------------------------------------------
## HTTP Listener
## The port that the Dashboard HTTP listener will bind.
##
## Value: Port
##
## Examples: 18083
dashboard.listener.http.port = 18083
## The acceptor pool for external Dashboard HTTP listener.
##
## Value: Number
dashboard.listener.http.acceptors = 4
## Maximum number of concurrent Dashboard HTTP connections.
##
## Value: Number
dashboard.listener.http.max_clients = 512
## Set up the socket for IPv6.
##
## Value: false | true
dashboard.listener.http.inet6 = 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
emqx_dashboard:{
default_username: "admin"
default_password: "public"
listeners: [
{
num_acceptors: 4
max_connections: 512
protocol: http
port: 18083
backlog: 512
send_timeout: 15s
send_timeout_close: true
inet6: false
ipv6_v6only: false
}
## ,
## {
## protocol: https
## port: 8081
## acceptors: 2
## backlog: 512
## send_timeout: 15s
## send_timeout_close: true
## inet6: false
## ipv6_v6only: false
## certfile = "etc/certs/cert.pem"
## keyfile = "etc/certs/key.pem"
## cacertfile = "etc/certs/cacert.pem"
## verify = verify_peer
## tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## 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
## inet6 = false
## ipv6_v6only = false
## }
]
}

View File

@ -14,7 +14,7 @@
%% limitations under the License.
%%--------------------------------------------------------------------
-record(mqtt_admin, {username, password, tags}).
-record(mqtt_admin, {username, password, tags, role = undefined}).
-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,
[{description, "EMQ X Web Dashboard"},
{vsn, "4.4.0"}, % strict semver, bump manually!
{vsn, "5.0.0"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_dashboard_sup]},
{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).
-include_lib("emqx/include/emqx.hrl").
-include_lib("emqx/include/logger.hrl").
-define(APP, ?MODULE).
%%-import(proplists, [get_value/3]).
-export([ start_listeners/0
, stop_listeners/0
, start_listener/1
, stop_listener/1
]).
, stop_listener/1]).
%% for minirest
-export([ filter/1
, is_authorized/1
]).
%% Authorization
-export([authorize_appid/1]).
-define(APP, ?MODULE).
-define(BASE_PATH, "/api/v5").
%%--------------------------------------------------------------------
%% Start/Stop listeners.
%% Start/Stop Listeners
%%--------------------------------------------------------------------
start_listeners() ->
lists:foreach(fun(Listener) -> start_listener(Listener) end, 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]}.
lists:foreach(fun start_listener/1, listeners()).
stop_listeners() ->
lists:foreach(fun(Listener) -> stop_listener(Listener) end, listeners()).
lists:foreach(fun stop_listener/1, listeners()).
stop_listener(_) ->
ok.
%% TODO: V5 API
%%stop_listener({Proto, _Port, _}) ->
%% minirest:stop_http(listener_name(Proto)).
start_listener({Proto, Port, Options}) ->
{ok, _} = application:ensure_all_started(minirest),
Authorization = {?MODULE, authorize_appid},
RanchOptions = ranch_opts(Port, Options),
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() ->
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) ->
%% list_to_atom(atom_to_list(Proto) ++ ":dashboard").
listener_name(Proto) ->
list_to_atom(atom_to_list(Proto) ++ ":dashboard").
%%--------------------------------------------------------------------
%% 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) ->
authorize_appid(Req) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of
{basic, Username, Password} ->
case emqx_dashboard_admin:check(iolist_to_binary(Username),
iolist_to_binary(Password)) of
ok -> true;
{error, Reason} ->
?LOG(error, "[Dashboard] Authorization Failure: username=~s, reason=~p",
[Username, Reason]),
false
ok ->
ok;
{error, _} ->
{401, #{<<"WWW-Authenticate">> =>
<<"Basic Realm=\"minirest-server\"">>},
<<"UNAUTHORIZED">>}
end;
_ -> false
_ ->
{401, #{<<"WWW-Authenticate">> =>
<<"Basic Realm=\"minirest-server\"">>},
<<"UNAUTHORIZED">>}
end.
filter(#{app := emqx_modules}) -> true;
filter(#{app := App}) ->
case emqx_plugins:find_plugin(App) of
false -> false;
Plugin -> Plugin#plugin.active
end.
format(Port) when is_integer(Port) ->
io_lib:format("0.0.0.0:~w", [Port]);
format({Addr, Port}) when is_list(Addr) ->
io_lib:format("~s:~w", [Addr, Port]);
format({Addr, Port}) when is_tuple(Addr) ->
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).

View File

@ -182,7 +182,7 @@ check(Username, Password) ->
init([]) ->
%% 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}.
handle_call(_Req, _From, State) ->
@ -217,7 +217,7 @@ salt() ->
<<Salt:32>>.
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) ->
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},
MinirestOptions = maps:merge(Minirest, RanchOptions),
{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 = [App || {App, _, _} <- application:loaded_applications(),

View File

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

View File

@ -15,22 +15,12 @@ main(_) ->
{ok, Bin} = file:read_file(BaseConf),
Apps = filelib:wildcard("emqx_*", "apps/"),
Conf = lists:foldl(fun(App, Acc) ->
case lists:member(App, ["emqx_exhook",
"emqx_exproto",
"emqx_lwm2m",
"emqx_sn",
"emqx_coap",
"emqx_stomp",
"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
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, Bin, Apps),
ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf).