%%%------------------------------------------------------------------- %%% @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 = <<" EventHub API Docs

EventHub API Documentation

">>, 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([ "", Title, "", "
", "", "", "" ]), 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, [], []}.