-module(admin_handler_tickets). -behaviour(cowboy_handler). -export([init/2]). -include("records.hrl"). init(Req, _Opts) -> case cowboy_req:binding(id, Req) of undefined -> handle_collection(Req); TicketId -> handle_item(TicketId, Req) end. handle_collection(Req) -> case cowboy_req:method(Req) of <<"GET">> -> list_tickets(Req); <<"POST">> -> create_ticket(Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. handle_item(TicketId, Req) -> case cowboy_req:method(Req) of <<"GET">> -> get_ticket(TicketId, Req); <<"PUT">> -> update_ticket(TicketId, Req); <<"DELETE">> -> delete_ticket(TicketId, Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. list_tickets(Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> Tickets = core_ticket:list_all(), % ← было list_tickets() send_json(Req1, 200, [ticket_to_json(T) || T <- Tickets]); {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. create_ticket(Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of #{<<"error_message">> := _} = Data -> % Администратор может указать error_hash, stacktrace, context, status TicketData = maps:merge(#{ <<"status">> => <<"open">>, <<"assigned_to">> => undefined }, Data), case core_ticket:create_ticket(TicketData) of {ok, Ticket} -> send_json(Req2, 201, ticket_to_json(Ticket)); {error, Reason} -> send_error(Req2, 500, Reason) end; _ -> send_error(Req2, 400, <<"Missing 'error_message' field">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. get_ticket(TicketId, Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> case core_ticket:get_by_id(TicketId) of {ok, Ticket} -> send_json(Req1, 200, ticket_to_json(Ticket)); {error, not_found} -> send_error(Req1, 404, <<"Ticket not found">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. update_ticket(TicketId, Req) -> case 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) -> case core_ticket:update_ticket(TicketId, UpdatesMap) of {ok, Ticket} -> send_json(Req2, 200, ticket_to_json(Ticket)); {error, not_found} -> send_error(Req2, 404, <<"Ticket not found">>); {error, Reason} -> send_error(Req2, 500, Reason) end; _ -> send_error(Req2, 400, <<"Invalid JSON">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. delete_ticket(TicketId, Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> case core_ticket:delete_ticket(TicketId) of {ok, deleted} -> send_json(Req1, 200, #{status => <<"deleted">>}); {error, not_found} -> send_error(Req1, 404, <<"Ticket not found">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. auth_admin(Req) -> case handler_auth:authenticate(Req) of {ok, AdminId, Req1} -> case is_admin(AdminId) of true -> {ok, AdminId, Req1}; false -> {error, 403, <<"Admin access required">>, Req1} end; {error, Code, Message, Req1} -> {error, Code, Message, Req1} end. is_admin(UserId) -> case core_user:get_by_id(UserId) of {ok, User} -> Role = User#user.role, Role =:= admin orelse Role =:= superadmin orelse Role =:= moderator orelse Role =:= support; _ -> false end. ticket_to_json(T) -> #{ id => T#ticket.id, error_hash => T#ticket.error_hash, error_message => T#ticket.error_message, stacktrace => T#ticket.stacktrace, context => T#ticket.context, count => T#ticket.count, first_seen => datetime_to_iso8601(T#ticket.first_seen), last_seen => datetime_to_iso8601(T#ticket.last_seen), status => T#ticket.status, assigned_to => T#ticket.assigned_to, resolution_note => T#ticket.resolution_note }. datetime_to_iso8601({{Year, Month, Day}, {Hour, Minute, Second}}) -> iolist_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ", [Year, Month, Day, Hour, Minute, Second])); datetime_to_iso8601(undefined) -> undefined. send_json(Req, Status, Data) -> Body = jsx:encode(Data), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}. send_error(Req, Status, Message) -> Body = jsx:encode(#{error => Message}), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}.