134 lines
5.1 KiB
Erlang
134 lines
5.1 KiB
Erlang
%%%-------------------------------------------------------------------
|
||
%%% @doc Обработчик документации Swagger.
|
||
%%%
|
||
%%% Раздаёт Swagger UI и спецификации OpenAPI для
|
||
%%% административного и клиентского API.
|
||
%%%
|
||
%%% GET / – индексная страница с выбором API
|
||
%%% GET /admin/ – Swagger UI для административного API
|
||
%%% GET /admin/swagger.json – OpenAPI-спецификация (admin)
|
||
%%% GET /user/ – Swagger UI для клиентского API
|
||
%%% GET /user/swagger.json – OpenAPI-спецификация (user)
|
||
%%% @end
|
||
%%%-------------------------------------------------------------------
|
||
-module(swagger_docs_handler).
|
||
-behaviour(cowboy_handler).
|
||
|
||
-export([init/2]).
|
||
|
||
%%% cowboy_handler callback
|
||
-spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
|
||
init(Req, _Opts) ->
|
||
Path = cowboy_req:path(Req),
|
||
handle(Path, Req).
|
||
|
||
%%--------------------------------------------------------------------
|
||
%% Роутинг путей
|
||
%%--------------------------------------------------------------------
|
||
|
||
-spec handle(binary(), cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
|
||
handle(<<"/">>, Req) ->
|
||
serve_index(Req);
|
||
handle(<<"/admin">>, Req) ->
|
||
redirect_to_slash(<<"/admin/">>, Req);
|
||
handle(<<"/admin/">>, Req) ->
|
||
serve_ui(admin, Req);
|
||
handle(<<"/admin/swagger.json">>, Req) ->
|
||
serve_json(admin, Req);
|
||
handle(<<"/user">>, Req) ->
|
||
redirect_to_slash(<<"/user/">>, Req);
|
||
handle(<<"/user/">>, Req) ->
|
||
serve_ui(user, Req);
|
||
handle(<<"/user/swagger.json">>, Req) ->
|
||
serve_json(user, Req);
|
||
handle(_, Req) ->
|
||
cowboy_req:reply(404, #{}, <<"Not Found">>, Req),
|
||
{ok, [], []}.
|
||
|
||
%%--------------------------------------------------------------------
|
||
%% Главная страница
|
||
%%--------------------------------------------------------------------
|
||
|
||
-spec serve_index(cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
|
||
serve_index(Req) ->
|
||
Html = <<"<!DOCTYPE html>
|
||
<html>
|
||
<head><title>EventHub API Docs</title></head>
|
||
<body>
|
||
<h1>EventHub API Documentation</h1>
|
||
<ul>
|
||
<li><a href=\"/admin/\">Admin API</a></li>
|
||
<li><a href=\"/user/\">User API</a></li>
|
||
</ul>
|
||
</body>
|
||
</html>">>,
|
||
cowboy_req:reply(200, #{<<"content-type">> => <<"text/html">>}, Html, Req),
|
||
{ok, Html, []}.
|
||
|
||
%%--------------------------------------------------------------------
|
||
%% Swagger UI
|
||
%%--------------------------------------------------------------------
|
||
|
||
-spec serve_ui(admin | user, cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
|
||
serve_ui(Api, Req) ->
|
||
{Title, SpecUrl} = case Api of
|
||
admin -> {<<"EventHub Admin API">>, <<"/admin/swagger.json">>};
|
||
user -> {<<"EventHub User API">>, <<"/user/swagger.json">>}
|
||
end,
|
||
Html = iolist_to_binary([
|
||
"<!DOCTYPE html><html><head><title>", Title,
|
||
"</title><link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\">",
|
||
"</head><body><div id=\"swagger-ui\"></div>",
|
||
"<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>",
|
||
"<script>window.onload=function(){SwaggerUIBundle({url:'", SpecUrl,
|
||
"',dom_id:'#swagger-ui',presets:[SwaggerUIBundle.presets.apis,SwaggerUIBundle.SwaggerUIStandalonePreset],layout:'BaseLayout'});}</script>",
|
||
"</body></html>"
|
||
]),
|
||
cowboy_req:reply(200, #{<<"content-type">> => <<"text/html">>}, Html, Req),
|
||
{ok, Html, []}.
|
||
|
||
%%--------------------------------------------------------------------
|
||
%% OpenAPI JSON
|
||
%%--------------------------------------------------------------------
|
||
|
||
-spec serve_json(admin | user, cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
|
||
serve_json(Api, Req) ->
|
||
Trails = case Api of
|
||
admin -> trails:admin();
|
||
user -> trails:user()
|
||
end,
|
||
OpenApi = #{
|
||
openapi => <<"3.0.3">>,
|
||
info => #{
|
||
title => case Api of
|
||
admin -> <<"EventHub Admin API">>;
|
||
user -> <<"EventHub User API">>
|
||
end,
|
||
version => <<"1.0.0">>
|
||
},
|
||
servers => [#{url => <<"http://localhost:8445">>, description => <<"API server">>}],
|
||
paths => build_paths(Trails)
|
||
},
|
||
Json = jsx:encode(OpenApi),
|
||
cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>}, Json, Req),
|
||
{ok, Json, []}.
|
||
|
||
%%--------------------------------------------------------------------
|
||
%% Вспомогательные функции
|
||
%%--------------------------------------------------------------------
|
||
|
||
-spec build_paths([map()]) -> map().
|
||
build_paths(Trails) ->
|
||
lists:foldl(fun(Trail, Acc) ->
|
||
Path = maps:get(path, Trail, <<"/">>),
|
||
Method0 = maps:get(method, Trail, <<"get">>),
|
||
Method = string:lowercase(Method0),
|
||
TrailData = maps:without([path, method], Trail),
|
||
PathItem = #{Method => TrailData},
|
||
maps:merge_with(fun(_, V1, V2) -> maps:merge(V1, V2) end, Acc, #{Path => PathItem})
|
||
end, #{}, Trails).
|
||
|
||
-spec redirect_to_slash(binary(), cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
|
||
redirect_to_slash(Location, Req) ->
|
||
cowboy_req:reply(301, #{<<"location">> => Location}, <<>>, Req),
|
||
{ok, [], []}. |