%%%------------------------------------------------------------------- %%% @doc Административный обработчик модерации. %%% PUT – применяет действие модерации к указанной сущности. %%% @end %%%------------------------------------------------------------------- -module(admin_handler_moderation). -behaviour(cowboy_handler). -export([init/2]). -export([trails/0]). -include("records.hrl"). -define(VALID_TARGETS, [<<"calendar">>, <<"event">>, <<"review">>, <<"user">>]). %%% cowboy_handler callback -spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}. init(Req, _Opts) -> case cowboy_req:method(Req) of <<"PUT">> -> moderate(Req); _ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>) end. %%% Swagger metadata -spec trails() -> [map()]. trails() -> Targets = [<<"calendar">>, <<"event">>, <<"review">>, <<"user">>], Actions = #{ <<"calendar">> => [<<"freeze">>, <<"unfreeze">>], <<"event">> => [<<"freeze">>, <<"unfreeze">>], <<"review">> => [<<"hide">>, <<"unhide">>], <<"user">> => [<<"block">>, <<"unblock">>] }, lists:flatmap(fun(Target) -> ActionList = maps:get(Target, Actions), lists:map(fun(Action) -> Path = <<"/v1/admin/", Target/binary, "/:id">>, #{ path => Path, method => <<"PUT">>, description => <<"Moderate ", Target/binary, " - ", Action/binary>>, tags => [<<"Moderation">>], parameters => [ #{ name => <<"target_type">>, in => <<"path">>, description => <<"Entity type">>, required => true, schema => #{type => string, enum => Targets} }, #{ name => <<"id">>, in => <<"path">>, description => <<"Entity ID">>, required => true, schema => #{type => string} } ], requestBody => #{ required => true, content => #{<<"application/json">> => #{schema => #{ type => object, required => [<<"action">>], properties => #{ action => #{type => string, enum => ActionList}, reason => #{type => string} } }}} }, responses => #{ 200 => #{description => <<"Moderation applied successfully">>}, 400 => #{description => <<"Bad request">>}, 404 => #{description => <<"Entity not found">>} } } end, ActionList) end, Targets). %%% Internal functions moderate(Req) -> case handler_utils:auth_admin(Req) of {ok, AdminId, Req1} -> TargetType = cowboy_req:binding(target_type, Req1), TargetId = cowboy_req:binding(id, Req1), case lists:member(TargetType, ?VALID_TARGETS) of true -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of #{<<"action">> := Action} = BodyMap -> Reason = maps:get(<<"reason">>, BodyMap, <<"">>), apply_moderation(TargetType, TargetId, Action, Reason, Req2, AdminId); _ -> handler_utils:send_error(Req2, 400, <<"Missing 'action' field">>) catch _:_ -> handler_utils:send_error(Req2, 400, <<"Invalid JSON">>) end; false -> handler_utils:send_error(Req1, 400, <<"Invalid target_type">>) end; {error, Code, Message, Req1} -> handler_utils:send_error(Req1, Code, Message) end. apply_moderation(<<"calendar">>, Id, Action, Reason, Req, AdminId) -> handle_calendar(Id, Action, Reason, Req, AdminId); apply_moderation(<<"event">>, Id, Action, Reason, Req, AdminId) -> handle_event(Id, Action, Reason, Req, AdminId); apply_moderation(<<"review">>, Id, Action, Reason, Req, AdminId) -> handle_review(Id, Action, Reason, Req, AdminId); apply_moderation(<<"user">>, Id, Action, Reason, Req, AdminId) -> handle_user(Id, Action, Reason, Req, AdminId). handle_calendar(Id, <<"freeze">>, Reason, Req, AdminId) -> case core_calendar:freeze(Id, Reason) of {ok, Calendar} -> log_audit(AdminId, <<"freeze_calendar">>, <<"calendar">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:calendar_to_json(Calendar)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Calendar not found">>) end; handle_calendar(Id, <<"unfreeze">>, Reason, Req, AdminId) -> case core_calendar:unfreeze(Id, Reason) of {ok, Calendar} -> log_audit(AdminId, <<"unfreeze_calendar">>, <<"calendar">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:calendar_to_json(Calendar)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Calendar not found">>) end; handle_calendar(_Id, _Action, _Reason, Req, _AdminId) -> handler_utils:send_error(Req, 400, <<"Invalid action for calendar">>). handle_event(Id, <<"freeze">>, Reason, Req, AdminId) -> case core_event:freeze(Id, Reason) of {ok, Event} -> log_audit(AdminId, <<"freeze_event">>, <<"event">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:event_to_json(Event)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Event not found">>) end; handle_event(Id, <<"unfreeze">>, Reason, Req, AdminId) -> case core_event:unfreeze(Id, Reason) of {ok, Event} -> log_audit(AdminId, <<"unfreeze_event">>, <<"event">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:event_to_json(Event)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Event not found">>) end; handle_event(_Id, _Action, _Reason, Req, _AdminId) -> handler_utils:send_error(Req, 400, <<"Invalid action for event">>). handle_review(Id, <<"hide">>, Reason, Req, AdminId) -> case core_review:hide(Id, Reason) of {ok, Review} -> log_audit(AdminId, <<"hide_review">>, <<"review">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:review_to_json(Review)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Review not found">>) end; handle_review(Id, <<"unhide">>, Reason, Req, AdminId) -> case core_review:unhide(Id, Reason) of {ok, Review} -> log_audit(AdminId, <<"unhide_review">>, <<"review">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:review_to_json(Review)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"Review not found">>) end; handle_review(_Id, _Action, _Reason, Req, _AdminId) -> handler_utils:send_error(Req, 400, <<"Invalid action for review">>). handle_user(Id, <<"block">>, Reason, Req, AdminId) -> case core_user:block(Id, Reason) of {ok, User} -> log_audit(AdminId, <<"block_user">>, <<"user">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:user_to_json(User)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"User not found">>) end; handle_user(Id, <<"unblock">>, Reason, Req, AdminId) -> case core_user:unblock(Id, Reason) of {ok, User} -> log_audit(AdminId, <<"unblock_user">>, <<"user">>, Id, Reason), handler_utils:send_json(Req, 200, handler_utils:user_to_json(User)); {error, not_found} -> handler_utils:send_error(Req, 404, <<"User not found">>) end; handle_user(_Id, _Action, _Reason, Req, _AdminId) -> handler_utils:send_error(Req, 400, <<"Invalid action for user">>). %% ── АУДИТ ────────────────────────────────────────────────── log_audit(AdminId, Action, EntityType, EntityId, Reason) -> case core_admin:get_by_id(AdminId) of {ok, Admin} -> core_admin_audit:log(AdminId, Admin#admin.email, Admin#admin.role, Action, EntityType, EntityId, client_ip(), Reason); _ -> ok end. %% ── ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ──────────────────────────────── client_ip() -> <<"127.0.0.1">>.