-module(admin_handler_banned_words). -behaviour(cowboy_handler). -export([init/2]). -include("records.hrl"). init(Req, _Opts) -> case cowboy_req:binding(word, Req) of undefined -> handle_collection(Req); Word -> handle_item(Word, Req) end. handle_collection(Req) -> case cowboy_req:method(Req) of <<"GET">> -> list_banned_words(Req); <<"POST">> -> add_banned_word(Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. handle_item(Word, Req) -> case cowboy_req:method(Req) of <<"DELETE">> -> delete_banned_word(Word, Req); <<"PUT">> -> update_banned_word(Word, Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. list_banned_words(Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> Words = core_banned_words:list_banned_words(), send_json(Req1, 200, [banned_word_to_json(W) || W <- Words]); {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. add_banned_word(Req) -> case auth_admin(Req) of {ok, AdminId, Req1} -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of #{<<"word">> := NewWord} -> case core_banned_words:add_banned_word(NewWord, AdminId) of {ok, WordRec} -> send_json(Req2, 201, banned_word_to_json(WordRec)); {error, already_exists} -> send_error(Req2, 409, <<"Word already exists">>); {error, _} -> send_error(Req2, 500, <<"Internal server error">>) end; _ -> send_error(Req2, 400, <<"Missing 'word' field">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. delete_banned_word(Word, Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> case core_banned_words:remove_banned_word(Word) of {ok, deleted} -> send_json(Req1, 200, #{status => <<"deleted">>}); {error, not_found} -> send_error(Req1, 404, <<"Word not found">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. update_banned_word(Word, Req) -> case auth_admin(Req) of {ok, _AdminId, Req1} -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of #{<<"word">> := NewWord} -> case core_banned_words:update_banned_word(Word, NewWord) of {ok, WordRec} -> send_json(Req2, 200, banned_word_to_json(WordRec)); {error, not_found} -> send_error(Req2, 404, <<"Word not found">>); {error, _} -> send_error(Req2, 500, <<"Internal server error">>) end; _ -> send_error(Req2, 400, <<"Missing 'word' field">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. auth_admin(Req) -> case handler_auth:authenticate(Req) of {ok, AdminId, Req1} -> case admin_utils: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. banned_word_to_json(BW) -> #{ id => BW#banned_word.id, word => BW#banned_word.word, added_by => BW#banned_word.added_by, added_at => datetime_to_iso8601(BW#banned_word.added_at) }. 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) -> Headers = #{ <<"content-type">> => <<"application/json">>, <<"access-control-allow-origin">> => <<"*">>, <<"access-control-expose-headers">> => <<"Content-Range">> }, Body = jsx:encode(Data), cowboy_req:reply(Status, Headers, 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, []}.