%%%------------------------------------------------------------------- %%% @doc Обработчик регистрации пользователя (клиентский API). %%% POST – создаёт нового пользователя (статус pending), возвращает %%% информацию о пользователе без JWT токена. %%% @end %%%------------------------------------------------------------------- -module(handler_register). -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) -> handle(Req, Opts). %%% Swagger metadata -spec trails() -> [map()]. trails() -> [ #{ path => <<"/v1/register">>, method => <<"POST">>, description => <<"Register a new user (account will be pending until email verified)">>, tags => [<<"Auth">>], requestBody => #{ required => true, content => #{<<"application/json">> => #{schema => #{ type => object, required => [<<"email">>, <<"password">>], properties => #{ email => #{type => string, format => <<"email">>}, password => #{type => string, format => <<"password">>} } }}} }, responses => #{ 201 => #{description => <<"User registered successfully (pending verification)">>}, 400 => #{description => <<"Missing email or password, or invalid JSON">>}, 409 => #{description => <<"Email already exists">>} } } ]. %%%=================================================================== %%% HTTP-методы %%%=================================================================== %% @private -spec handle(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}. handle(Req, _Opts) -> case cowboy_req:method(Req) of <<"POST">> -> register(Req); _ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>) end. %% @doc POST /v1/register — регистрация нового пользователя. -spec register(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. register(Req) -> case cowboy_req:has_body(Req) of true -> {ok, Body, Req1} = cowboy_req:read_body(Req), case Body of <<>> -> handler_utils:send_error(Req1, 400, <<"Empty request body">>); _ -> try jsx:decode(Body, [return_maps]) of #{<<"email">> := Email, <<"password">> := Password} -> case core_user:email_exists(Email) of true -> handler_utils:send_error(Req1, 409, <<"Email already exists">>); false -> case core_user:create(Email, Password) of {ok, User} -> % Создаём верификационный токен и отправляем письмо {ok, VerificationToken, _Expires} = core_verification:create_token(User#user.id), logic_email:send_verification_email(Email, VerificationToken), Response = #{ user => #{ id => User#user.id, email => User#user.email, role => atom_to_binary(User#user.role, utf8), status => atom_to_binary(User#user.status, utf8) % <<"pending">> } }, handler_utils:send_json(Req1, 201, Response); {error, email_exists} -> handler_utils:send_error(Req1, 409, <<"Email already exists">>); {error, _} -> handler_utils:send_error(Req1, 500, <<"Internal server error">>) end end; _ -> handler_utils:send_error(Req1, 400, <<"Missing email or password">>) catch _:_ -> handler_utils:send_error(Req1, 400, <<"Invalid JSON">>) end end; false -> handler_utils:send_error(Req, 400, <<"Missing request body">>) end.