%%%-------------------------------------------------------------------
%% @doc Cowboy handler for the read-only administration dashboard.
%%%-------------------------------------------------------------------
-module(vpn_dashboard_http).
-export([init/2]).
init(Req0, State) ->
case cowboy_req:method(Req0) of
<<"GET">> ->
reply_dashboard(Req0, State);
_Other ->
Req = cowboy_req:reply(
405,
#{<<"allow">> => <<"GET">>},
Req0),
{ok, Req, State}
end.
reply_dashboard(Req0, State) ->
try render(vpn_admin:summary_view()) of
Html ->
Req = cowboy_req:reply(
200,
#{<<"content-type">> => <<"text/html; charset=utf-8">>},
Html,
Req0),
{ok, Req, State}
catch
_:_ ->
Req = cowboy_req:reply(
500,
#{<<"content-type">> => <<"text/html; charset=utf-8">>},
<<"
VPN Dashboard Error"
"VPN Dashboard Error
">>,
Req0),
{ok, Req, State}
end.
render(Summary) ->
Counts = maps:get(counts, Summary, #{}),
Peers = maps:get(peers, Summary, []),
iolist_to_binary([
<<"">>,
<<"">>,
<<"VPN Dashboard">>,
style(),
<<"">>,
<<"VPN Dashboard
">>,
actions(),
counts(Counts),
peers_table(Peers),
<<"">>
]).
style() ->
<<"">>.
actions() ->
<<"">>.
counts(Counts) ->
[
<<"">>,
count_card(<<"Configured Peers">>, maps:get(configured, Counts, 0)),
count_card(<<"Running Peers">>, maps:get(running, Counts, 0)),
count_card(<<"Stopped Peers">>, maps:get(stopped, Counts, 0)),
count_card(<<"Certificates">>, maps:get(certificates, Counts, 0)),
<<"">>
].
count_card(Label, Value) ->
[<<"">>, html_escape(Label), <<"">>,
html_escape(Value), <<"
">>].
peers_table(Peers) ->
[
<<"">>,
table_header(<<"Peer">>),
table_header(<<"Running">>),
table_header(<<"Mode">>),
table_header(<<"IP">>),
table_header(<<"Remote Peer">>),
table_header(<<"Trusted">>),
table_header(<<"Key Match">>),
table_header(<<"Expires">>),
table_header(<<"Crypto Failures">>),
table_header(<<"Frames Rejected">>),
table_header(<<"Actions">>),
<<"
">>,
[peer_row(Peer) || Peer <- Peers],
<<"
">>
].
table_header(Label) ->
[<<"">>, html_escape(Label), <<" | ">>].
peer_row(Peer) ->
Certificate = maps:get(certificate, Peer, #{}),
[
<<"">>,
table_cell(maps:get(id, Peer, null)),
table_cell(yes_no(maps:get(running, Peer, false))),
table_cell(maps:get(mode, Peer, null)),
table_cell(maps:get(ip, Peer, null)),
table_cell(maps:get(remote_peer_id, Peer, null)),
table_cell(yes_no(maps:get(trusted, Certificate, false))),
table_cell(yes_no(maps:get(key_match, Certificate, false))),
table_cell(maps:get(not_after, Certificate, null)),
table_cell(maps:get(crypto_failures, Peer, 0)),
table_cell(maps:get(frames_rejected, Peer, 0)),
table_cell_raw(peer_action(Peer)),
<<"
">>
].
table_cell(Value) ->
[<<"">>, html_escape(Value), <<" | ">>].
table_cell_raw(Html) ->
[<<"">>, Html, <<" | ">>].
peer_action(Peer) ->
PeerId = maps:get(id, Peer, null),
Running = maps:get(running, Peer, false),
{Action, Label} =
case Running of
true ->
{<<"stop">>, <<"Stop">>};
false ->
{<<"start">>, <<"Start">>}
end,
[<<"">>].
yes_no(true) ->
<<"yes">>;
yes_no(false) ->
<<"no">>;
yes_no(_Value) ->
<<"no">>.
html_escape(null) ->
<<>>;
html_escape(Value) when is_binary(Value) ->
escape_binary(Value);
html_escape(Value) when is_integer(Value) ->
integer_to_binary(Value);
html_escape(Value) when is_float(Value) ->
float_to_binary(Value, [{decimals, 6}, compact]);
html_escape(true) ->
<<"true">>;
html_escape(false) ->
<<"false">>;
html_escape(Value) when is_atom(Value) ->
escape_binary(atom_to_binary(Value, utf8));
html_escape(Value) when is_list(Value) ->
escape_binary(unicode:characters_to_binary(Value));
html_escape(_Value) ->
<<>>.
escape_binary(Value) ->
escape_binary(Value, []).
escape_binary(<<>>, Acc) ->
iolist_to_binary(lists:reverse(Acc));
escape_binary(<<"&", Rest/binary>>, Acc) ->
escape_binary(Rest, [<<"&">> | Acc]);
escape_binary(<<"<", Rest/binary>>, Acc) ->
escape_binary(Rest, [<<"<">> | Acc]);
escape_binary(<<">", Rest/binary>>, Acc) ->
escape_binary(Rest, [<<">">> | Acc]);
escape_binary(<<"\"", Rest/binary>>, Acc) ->
escape_binary(Rest, [<<""">> | Acc]);
escape_binary(<<"'", Rest/binary>>, Acc) ->
escape_binary(Rest, [<<"'">> | Acc]);
escape_binary(<>, Acc) ->
escape_binary(Rest, [<> | Acc]).