%%%------------------------------------------------------------------- %%% @doc Административный обработчик профиля текущего администратора. %%% GET – получить свой профиль. %%% PUT – обновить свой профиль. %%% @end %%%------------------------------------------------------------------- -module(admin_handler_me). -behaviour(cowboy_handler). -export([init/2]). -export([trails/0]). -include("records.hrl"). %%% cowboy_handler callback -spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}. init(Req, _Opts) -> case cowboy_req:method(Req) of <<"GET">> -> get_me(Req); <<"PUT">> -> update_me(Req); _ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>) end. %%% Swagger metadata -spec trails() -> [map()]. trails() -> [ #{ % GET path => <<"/v1/admin/me">>, method => <<"GET">>, description => <<"Get current admin profile">>, tags => [<<"Admins">>], responses => #{ 200 => #{ description => <<"Admin profile">>, content => #{<<"application/json">> => #{schema => admin_schema()}} } } }, #{ % PUT path => <<"/v1/admin/me">>, method => <<"PUT">>, description => <<"Update current admin profile">>, tags => [<<"Admins">>], requestBody => #{ required => true, content => #{<<"application/json">> => #{schema => admin_update_schema()}} }, responses => #{ 200 => #{description => <<"Updated profile">>} } } ]. -spec admin_schema() -> map(). admin_schema() -> #{ type => object, properties => #{ id => #{type => string}, email => #{type => string}, role => #{type => string}, status => #{type => string}, nickname => #{type => string, nullable => true}, avatar_url => #{type => string, nullable => true}, timezone => #{type => string, nullable => true}, language => #{type => string, nullable => true}, phone => #{type => string, nullable => true}, preferences => #{type => object, nullable => true}, last_login => #{type => string, format => <<"date-time">>}, created_at => #{type => string, format => <<"date-time">>}, updated_at => #{type => string, format => <<"date-time">>} } }. -spec admin_update_schema() -> map(). admin_update_schema() -> #{ type => object, properties => #{ nickname => #{type => string}, avatar_url => #{type => string}, timezone => #{type => string}, language => #{type => string}, phone => #{type => string}, preferences => #{type => object} } }. %%% Internal functions -spec get_me(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. get_me(Req) -> case handler_utils:auth_admin(Req) of {ok, AdminId, Req1} -> case logic_admin:get_admin(AdminId) of {ok, Admin} -> handler_utils:send_json(Req1, 200, handler_utils:admin_to_json(Admin)); {error, not_found} -> handler_utils:send_error(Req1, 404, <<"Admin not found">>); {error, _} -> handler_utils:send_error(Req1, 500, <<"Internal server error">>) end; {error, Code, Msg, Req1} -> handler_utils:send_error(Req1, Code, Msg) end. -spec update_me(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. update_me(Req) -> case handler_utils:auth_admin(Req) of {ok, AdminId, Req1} -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of UpdatesMap when is_map(UpdatesMap) -> Updates0 = maps:to_list(UpdatesMap), Updates = convert_admin_fields(Updates0), case logic_admin:update_admin(AdminId, Updates) of {ok, Admin} -> % Аудит обновления профиля admin_utils:log_admin_action(AdminId, <<"update_me">>, <<"admin">>, AdminId, Req2), handler_utils:send_json(Req2, 200, handler_utils:admin_to_json(Admin)); {error, not_found} -> handler_utils:send_error(Req2, 404, <<"Admin not found">>); {error, _} -> handler_utils:send_error(Req2, 500, <<"Internal server error">>) end; _ -> handler_utils:send_error(Req2, 400, <<"Invalid JSON">>) catch _:_ -> handler_utils:send_error(Req1, 400, <<"Invalid JSON format">>) end; {error, Code, Msg, Req1} -> handler_utils:send_error(Req1, Code, Msg) end. %% @private Преобразует бинарные ключи в атомы для обновлений администратора. -spec convert_admin_fields([{binary(), term()}]) -> [{atom(), term()}]. convert_admin_fields(Updates) -> lists:map(fun ({<<"nickname">>, V}) -> {nickname, V}; ({<<"avatar_url">>, V}) -> {avatar_url, V}; ({<<"timezone">>, V}) -> {timezone, V}; ({<<"language">>, V}) -> {language, V}; ({<<"phone">>, V}) -> {phone, V}; ({<<"preferences">>, V}) -> {preferences, V}; (Other) -> Other end, Updates).