|
|
@@ -20,9 +20,7 @@
|
|
|
|
|
|
|
|
|
-export([ start_listeners/0
|
|
|
- , stop_listeners/0
|
|
|
- , start_listener/1
|
|
|
- , stop_listener/1]).
|
|
|
+ , stop_listeners/0]).
|
|
|
|
|
|
%% Authorization
|
|
|
-export([authorize_appid/1]).
|
|
|
@@ -36,15 +34,8 @@
|
|
|
%%--------------------------------------------------------------------
|
|
|
|
|
|
start_listeners() ->
|
|
|
- lists:foreach(fun start_listener/1, listeners()).
|
|
|
-
|
|
|
-stop_listeners() ->
|
|
|
- lists:foreach(fun stop_listener/1, listeners()).
|
|
|
-
|
|
|
-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"},
|
|
|
@@ -56,20 +47,33 @@ start_listener({Proto, Port, Options}) ->
|
|
|
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"}},
|
|
|
- {'_', cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}}],
|
|
|
- Minirest = #{
|
|
|
- protocol => Proto,
|
|
|
+ Dispatch = [
|
|
|
+ {"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
|
|
|
+ {"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
|
|
|
+ {'_', cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}}
|
|
|
+ ],
|
|
|
+ BaseMinirest = #{
|
|
|
base_path => ?BASE_PATH,
|
|
|
modules => minirest_api:find_api_modules(apps()),
|
|
|
authorization => Authorization,
|
|
|
security => [#{application => []}],
|
|
|
swagger_global_spec => GlobalSpec,
|
|
|
- dispatch => Dispatch},
|
|
|
- MinirestOptions = maps:merge(Minirest, RanchOptions),
|
|
|
- {ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
|
|
|
- ?ULOG("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
|
|
|
+ dispatch => Dispatch
|
|
|
+ },
|
|
|
+ [begin
|
|
|
+ Minirest = maps:put(protocol, Protocol, BaseMinirest),
|
|
|
+ {ok, _} = minirest:start(Name, RanchOptions, Minirest),
|
|
|
+ ?ULOG("Start listener ~s on ~p successfully.~n", [Name, Port])
|
|
|
+ end || {Name, Protocol, Port, RanchOptions} <- listeners()].
|
|
|
+
|
|
|
+stop_listeners() ->
|
|
|
+ [begin
|
|
|
+ ok = minirest:stop(Name),
|
|
|
+ ?ULOG("Stop listener ~s on ~p successfully.~n", [Name, Port])
|
|
|
+ end || {Name, _, Port, _} <- listeners()].
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% internal
|
|
|
|
|
|
apps() ->
|
|
|
[App || {App, _, _} <- application:loaded_applications(),
|
|
|
@@ -78,30 +82,48 @@ apps() ->
|
|
|
_ -> 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, _}) ->
|
|
|
- ?ULOG("Stop dashboard listener on ~s successfully.~n", [format(Port)]),
|
|
|
- minirest:stop(listener_name(Proto)).
|
|
|
-
|
|
|
listeners() ->
|
|
|
- [{Protocol, Port, maps:to_list(maps:without([protocol, port], Map))}
|
|
|
- || Map = #{protocol := Protocol,port := Port}
|
|
|
- <- emqx:get_config([emqx_dashboard, listeners], [])].
|
|
|
+ [begin
|
|
|
+ Protocol = maps:get(protocol, ListenerOptions, http),
|
|
|
+ Port = maps:get(port, ListenerOptions, 18083),
|
|
|
+ Name = listener_name(Protocol, Port),
|
|
|
+ RanchOptions = ranch_opts(maps:without([protocol], ListenerOptions)),
|
|
|
+ {Name, Protocol, Port, RanchOptions}
|
|
|
+ end || ListenerOptions <- emqx_config:get([emqx_dashboard, listeners], [])].
|
|
|
+
|
|
|
+ranch_opts(RanchOptions) ->
|
|
|
+ Keys = [ {ack_timeout, handshake_timeout}
|
|
|
+ , connection_type
|
|
|
+ , max_connections
|
|
|
+ , num_acceptors
|
|
|
+ , shutdown
|
|
|
+ , socket],
|
|
|
+ {S, R} = lists:foldl(fun key_take/2, {RanchOptions, #{}}, Keys),
|
|
|
+ R#{socket_opts => maps:fold(fun key_only/3, [], S)}.
|
|
|
+
|
|
|
+
|
|
|
+key_take({K, K1}, {All, R}) ->
|
|
|
+ case maps:get(K, All, undefined) of
|
|
|
+ undefined ->
|
|
|
+ {All, R};
|
|
|
+ V ->
|
|
|
+ {maps:remove(K, All), R#{K1 => V}}
|
|
|
+ end;
|
|
|
+key_take(K, {All, R}) ->
|
|
|
+ case maps:get(K, All, undefined) of
|
|
|
+ undefined ->
|
|
|
+ {All, R};
|
|
|
+ V ->
|
|
|
+ {maps:remove(K, All), R#{K => V}}
|
|
|
+ end.
|
|
|
|
|
|
-listener_name(Proto) ->
|
|
|
- list_to_atom(atom_to_list(Proto) ++ ":dashboard").
|
|
|
+key_only(K , true , S) -> [K | S];
|
|
|
+key_only(_K, false, S) -> S;
|
|
|
+key_only(K , V , S) -> [{K, V} | S].
|
|
|
+
|
|
|
+listener_name(Protocol, Port) ->
|
|
|
+ Name = "dashboard:" ++ atom_to_list(Protocol) ++ ":" ++ integer_to_list(Port),
|
|
|
+ list_to_atom(Name).
|
|
|
|
|
|
authorize_appid(Req) ->
|
|
|
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
|
|
@@ -127,10 +149,3 @@ authorize_appid(Req) ->
|
|
|
#{code => <<"UNAUTHORIZED">>,
|
|
|
message => <<"POST '/login'">>}}
|
|
|
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]).
|